一点背景:
ECMAScript 6+ 区分 callable(可以不用new 调用)和constructable(可以用new 调用)函数:
- 通过箭头函数语法或通过类或对象字面量中的方法定义创建的函数不可构造。
- 通过
class 语法创建的函数不可调用。
- 以任何其他方式(函数表达式/声明、
Function 构造函数)创建的函数都是可调用和可构造的。
- 除非另有明确说明,否则内置函数不可构造。
关于Function.prototype
Function.prototype 是所谓的built-in function that is not constructable。来自规范:
未标识为构造函数的内置函数对象不实现[[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()');