我注意到并非所有的Javascript函数都是构造函数。
var obj = Function.prototype;
console.log(typeof obj === 'function'); //true
obj(); //OK
new obj(); //TypeError: obj is not a constructor
问题1:如何检查函数是否为构造函数,以便可以使用新函数调用它?
问题2:当我创建一个函数时,是否可以使 NOT 成为构造函数?
答案 0 :(得分:38)
一点背景知识:
ECMAScript 6+区分可调用(可以不带new
调用)和可构造(可以使用new
调用)函数:< / p>
class
语法创建的函数无法调用。Function
构造函数)是可调用和可构造的。 关于Function.prototype
Function.prototype
是一个所谓的built-in function that is not constructable。来自规范:
未标识为构造函数的内置函数对象不实现
[[Construct]]
内部方法,除非在特定函数的描述中另有说明。
Function.prototype
的值是在运行时初始化的最开始创建的。它基本上是一个空函数,并没有明确说明它是可构造的。
如何检查函数是否为构造函数,以便可以使用new调用它?
没有内置的方法可以做到这一点。您可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');
根据定义,箭头函数不可构造。或者,您可以将函数定义为对象或类的方法。
否则,您可以通过检查new
this
值来检查是否使用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()');
的值,因此可能存在误报。
<强>实施例强>
ngrx-store-freeze
&#13;
答案 1 :(得分:8)
有一种快速简便的方法可以确定函数是否可以实例化,而不必使用try-catch语句(v8无法优化)
function isConstructor(obj) {
return !!obj.prototype && !!obj.prototype.constructor.name;
}
有一个警告,即:functions named inside a definition仍会产生名称属性,从而通过此检查,因此在依赖函数构造函数的测试时需要谨慎。
在下面的示例中,该函数不是匿名函数,但实际上称为“myFunc”。它的原型可以像任何JS类一样扩展。
let myFunc = function () {};
:)
答案 2 :(得分:6)
您正在寻找函数是否具有[[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
内部方法调用。)
我发现它用于String
函数的newTarget
(js中的new.target
),可与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)
&LT;请注意&GT;
我发现它不起作用的唯一值是Symbol
,虽然new Symbol
在Firefox中引发了TypeError: Symbol is not a constructor
is_constructor(Symbol) === true
。这是技术上正确答案,因为Symbol
具有[[Construct]]
内部方法(这意味着它也可以是子类),但使用{ {1}}或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.
或其他标志之类的东西。)
&LT; /注释&GT;
答案 3 :(得分:6)
使用ES6 + Proxies,可以在不实际调用构造函数的情况下测试[[Construct]]
。这是一个片段:
const handler={construct(){return handler}} //Must return ANY object, so reuse one
const isConstructor=x=>{
try{
return !!(new (new Proxy(x,handler))())
}catch(e){
return false
}
}
如果传递的项目不是对象,则Proxy
构造函数会抛出错误。如果它不是可构造对象,则new
会抛出错误。但是如果它是一个可构造的对象,那么它会返回handler
对象,而不会调用它的构造函数,然后它不会被注入true
。
正如您所料,Symbol
仍被视为构造函数。这是因为它是,并且实现仅在调用[[Construct]]
时抛出错误。这可能是任何用户定义的函数在new.target
存在时抛出错误的情况,因此将其作为附加检查专门清除它似乎没有问题,但如果你这样做,请随意这样做发现这很有帮助。
答案 4 :(得分:1)
如果函数是构造函数,则它将具有“原型”成员,而该成员又具有与函数本身相同的“构造函数”成员。
function isConstructor(func) {
return (func && typeof func === "function" && func.prototype && func.prototype.constructor) === func;
}
答案 5 :(得分:0)
对于问题1,这个助手呢?
Function.isConstructor = ({ prototype }) => Boolean(prototype) && Boolean(prototype.constructor)
Function.isConstructor(class {}); // true
Function.isConstructor(function() {}); // true
Function.isConstructor(() => {}); // false
Function.isConstructor("a string"); // false
对于问题2,箭头功能是解决方案。它不能用作构造函数,因为它不依赖于与常规函数相同的作用域,并且没有原型(实例的定义,类似于真实OOP的类定义)
const constructable = function() { console.log(this); };
const callable = () => { console.log(this); };
constructable(); // Window {}
callable(); // Window {}
new constructable(); // aConstructableFunction {}
new callable(); // Uncaught TypeError: callable is not a constructor
答案 6 :(得分:0)
作为Felix Kling's answer的补充,即使一个函数不可构造,如果它具有prototype
属性,我们仍然可以像构造函数一样使用它。我们可以借助Object.create()
来做到这一点。示例:
// The built-in object Symbol is not constructable, even though it has a "prototype" property:
new Symbol
// TypeError: Symbol is not a constructor.
Object.create(Symbol.prototype);
// Symbol {}
// description: (...)
// __proto__: Symbol