【问题标题】:How to recognize the function is the class constructor and how to call it as a function?如何识别函数是类构造函数以及如何将其作为函数调用?
【发布时间】:2016-11-17 13:46:03
【问题描述】:

我对 es2016 中的 class 功能有点困惑,尽管与 function 和 prototype 相比,它被认为只是用于创建类的语法糖,但在某些情况下的行为是不同的,特别是 - 类不能像函数一样被调用,而且看起来,如果不使用 toString 和 /^class/ @987654327,就无法确定函数是类构造函数还是简单函数@。

假设示例:

class Foo {
    constructor () {
        this.name = 'foo';
    }
}
function Bar () {
    this.name = 'bar';
}

function doSmth (anyArg) {
    if (typeof anyArg === 'function') {
        var obj = { someProp: 'qux' };
        anyArg.call(obj);
        return obj;
    }
    // ...
}


doSmth(Bar);
doSmth(Foo); // Class constructor Foo cannot be invoked without 'new'

是typeof 'function',但不能作为函数调用!好的。

这是我的两个问题:

  1. 有什么方法可以调用 Foo 构造函数,与 Bar 相同,并覆盖 this 上下文?
  2. 有什么方法可以检测到anyArg 是一个类的构造函数,这样我就可以在我的doSmth 函数中以不同的方式处理它。如果没有toString 和RegExp(在这种情况下性能损失会很大)。然后,我可以使用 Reflect.construct 初始化新实例,并使用 Object.assign 使用实例中的值扩展我的 obj 变量。

谢谢你,亚历克斯

【问题讨论】:

标签: javascript es6-class


【解决方案1】:

两个问题都否。

以下是 Angular 1.x 检测类的方式:

function isClass(func) {
  // IE 9-11 do not support classes and IE9 leaks with the code below.
  if (msie <= 11 || typeof func !== 'function') {
    return false;
  }
  var result = func.$$ngIsClass;
  if (!isBoolean(result)) {
    // Support: Edge 12-13 only
    // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
    result = func.$$ngIsClass = /^(?:class\b|constructor\()/.test(stringifyFn(func));
  }
  return result;
}

很遗憾,这是最好的解决方案。而且它甚至在 Firefox 中也不起作用。

【讨论】:

  • 感谢您提供来自 Angular 的示例。是的,似乎它确实是检测类的唯一方法。如果没有更好的解决方案有人会接受这个作为答案。
【解决方案2】:

我不知道如何做你在第一个问题中提出的问题。

对于第二个问题,您实际上确定了一种方法来自己区分差异。类构造函数和用作构造函数的函数之间的区别之一是前者在不带new关键字的情况下使用会出错,而后者则不会。

所以,如果我们使用 try/catch,我们可以大致做你想做的事:

class Foo {
    constructor () {
        this.name = 'foo';
    }
}
function Bar () {
    this.name = 'bar';
}

function doSmth (anyArg) {
    if (typeof anyArg === 'function') {
        var obj = { someProp: 'qux' };
        try {
          anyArg.call(obj);
        } catch(e) {
          var x = Reflect.construct(anyArg, []);
          Object.assign(obj, x);
        }
        return obj;
    }
}


doSmth(Bar);
doSmth(Foo);

明确地说,我并不是说这是一个好主意、好代码,或者它具有您正在寻找的性能,但我想我会指出这种可能性确实存在。

【讨论】:

  • try..catch 将是一个解决方案,但在这里我们不能确定异常是由调用类作为函数引起的,而不是函数内部的某些代码引起的。从您的示例中,我们应该以某种方式解析异常消息,但是消息是否跨浏览器统一?
  • 当然。您要么必须这样做,要么使用更本地的 try/catch 覆盖调用的函数内的所有代码,这有其自身的问题。就像我说的那样,我并不是说这种方法是完美的(老实说,我怀疑你会找到一个完美的解决方案),只是想提高完整性的可能性,以防它帮助其他访问者将来的页面。
【解决方案3】:

如果您担心 RegEx 的性能损失,请使用substring()。如果您仍然关心字符串操作的性能,那么请考虑另一种实际区分函数类型的语言。在 JavaScript 中,它们都是 function。

class Foo {
  constructor() {
    this.name = 'foo';
  }
}

function Bar() {
  this.name = 'bar';
}

function doSmth(anyArg) {
  if (typeof anyArg === 'function') {
    var obj = {
      someProp: 'qux'
    };
    if (anyArg.toString().substring(0, 5) === 'class') {
      Object.assign(obj, new anyArg());
    } else {
      anyArg.call(obj);
    }
    return obj;
  }
  // ...
}

var bar = doSmth(Bar);
var foo = doSmth(Foo);

console.log(bar);
console.log(foo);

【讨论】:

  • anyArg.constructor === Function 所以anyArg.constructor() 总是返回一个无操作函数。
猜你喜欢
  • 1970-01-01
  • 2018-03-29
  • 2010-12-06
  • 2021-06-15
  • 1970-01-01
  • 2021-07-28
  • 2021-03-14
  • 1970-01-01
相关资源
最近更新 更多