如何检查 Javascript 函数是否是构造函数

新手上路,请多包涵

我注意到并非所有 Javascript 函数都是构造函数。

 var obj = Function.prototype;
console.log(typeof obj === 'function'); //true
obj(); //OK
new obj(); //TypeError: obj is not a constructor

问题 1:如何检查函数是否为构造函数,以便可以使用 new 关键字调用它?

问题 2:当我创建一个函数时,是否可以让它 不是 构造函数?

原文由 Mingtao Sun 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

一点背景:

ECMAScript 6+ 区分 _可调用_(可以在没有 new 下调用)和可 _构造的_(可以通过 new 调用)函数:

  • 通过箭头函数语法或通过类或对象文字中的方法定义创建的函数是 _不可构造的_。
  • 通过 class 语法创建的函数 _不可调用_。
  • 以任何其他方式创建的函数(函数表达式/声明, Function 构造函数)是可调用和可构造的。
  • 除非另有明确说明,否则内置函数不可构造。

关于 Function.prototype

Function.prototype 是所谓的 不可构造的 内置函数。从规格:

未标识为构造函数的内置函数对象不实现 [[Construct]] 内部方法,除非在特定函数的描述中另有说明。

Function.prototype 的值是在运行时初始化的最开始时创建的。它基本上是一个空函数,并且没有明确声明它是可构造的。


我如何检查一个函数是否是一个构造函数,以便它可以用一个新的调用?

没有内置的方法可以做到这一点。您可以 try 调用函数 new ,然后检查错误或返回 true

 function isConstructor(f) {
  try {
    new f();
  } catch (err) {
    // verify err is the expected error and then
    return false;
  }
  return true;
}

但是,该方法不是故障安全的,因为函数可能有副作用,因此在调用 f 之后,您不知道环境处于哪种状态。

此外,这只会告诉你一个函数是否 可以 作为构造函数调用,而不是它是否 打算 作为构造函数调用。为此,您必须查看文档或函数的实现。

注意: 永远不应该有理由在生产环境中使用这样的测试。一个函数是否应该用 new 调用应该可以从它的文档中辨别出来。

当我创建一个函数时,如何使它不是构造函数?

要创建一个真正不可 构造的 函数,您可以使用箭头函数:

 var f = () => console.log('no constructable');

根据定义,箭头函数是不可构造的。或者,您可以将函数定义为对象或类的方法。

否则,您可以通过检查它的 --- this 值来检查是否使用 new (或类似的东西)调用函数,如果是,则抛出错误:

 function foo() {
  if (this instanceof foo) {
    throw new Error("Don't call 'foo' with new");
  }
}

当然,由于还有其他方法可以设置 this 的值,因此可能会出现误报。


例子

 function isConstructor(f) {
  try {
    new f();
  } catch (err) {
    if (err.message.indexOf('is not a constructor') >= 0) {
      return false;
    }
  }
  return true;
}

function test(f, name) {
  console.log(`${name} is constructable: ${isConstructor(f)}`);
}

function foo(){}
test(foo, 'function declaration');
test(function(){}, 'function expression');
test(()=>{}, 'arrow function');

class Foo {}
test(Foo, 'class declaration');
test(class {}, 'class expression');

test({foo(){}}.foo, 'object method');

class Foo2 {
  static bar() {}
  bar() {}
}
test(Foo2.bar, 'static class method');
test(new Foo2().bar, 'class method');

test(new Function(), 'new Function()');

原文由 Felix Kling 发布,翻译遵循 CC BY-SA 3.0 许可协议

您正在寻找函数是否具有 [[Construct]] 内部方法。内部方法 IsConstructor 详细步骤:

IsConstructor(argument)

>  ReturnIfAbrupt(argument).  // (Check if an exception has been thrown; Not important.)
> If Type(argument) is not Object, return false.  // argument === Object(argument), or (typeof argument === 'Object' || typeof argument === 'function')
> If argument has a [[Construct]] internal method, return true.
> Return false.
>
> ```

现在我们需要找到使用 `IsConstructor` 的地方,但是没有调用 `[[Construct]]` 地方(通常由 [`Construct`](http://www.ecma-international.org/ecma-262/6.0/#sec-construct) 内部方法调用。)

I found that it is used in the `String` function's `newTarget` ( [`new.target`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target) in js), which can be used with [`Reflect.construct`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct) :

function is_constructor(f) { try { Reflect.construct(String, [], f); } catch (e) { return false; } return true; }


(我真的可以使用任何东西,比如 `Reflect.construct(Array, [], f);` ,但是 `String` 是第一个)

产生以下结果:

// true is_constructor(function(){}); is_constructor(class A {}); is_constructor(Array); is_constructor(Function); is_constructor(new Function);

// false is_constructor(); is_constructor(undefined); is_constructor(null); is_constructor(1); is_constructor(new Number(1)); is_constructor(Array.prototype); is_constructor(Function.prototype); is_constructor(() => {}) is_constructor({method() {}}.method)


* * *

<注意>

The only value that I found it didn't work for is `Symbol` , which, although `new Symbol` throws a `TypeError: Symbol is not a constructor` in Firefox, `is_constructor(Symbol) === true` .这 _在技术上_ 是正确的答案,因为 `Symbol` _确实_ 有一个 `[[Construct]]` 内部方法(这意味着它也可以被子类化),但使用 `new` `super` 是 `Symbol` 抛出错误的特殊情况(因此, `Symbol` 是一个构造函数,错误消息是错误的,它只是不能。)不过,您可以将 `if (f === Symbol) return false;` 添加到顶部。

对于这样的事情也是一样的:

function not_a_constructor() { if (new.target) throw new TypeError(‘not_a_constructor is not a constructor.’); return stuff(arguments); }

is_constructor(not_a_constructor); // true new not_a_constructor; // TypeError: not_a_constructor is not a constructor.

”`

因此,作为构造函数的函数的意图不能像这样被理解(直到添加诸如 Symbol.is_constructor 或其他一些标志之类的东西)。

</注意>

原文由 Artyer 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题