我注意到并非所有的Javascript函数都是构造函数.
var obj = Function.prototype;
console.log(typeof obj === 'function'); //true
obj(); //OK
new obj(); //TypeError: obj is not a constructor
Run Code Online (Sandbox Code Playgroud)
问题1:如何检查函数是否为构造函数,以便可以使用new调用它?
问题2:当我创建一个函数时,是否可以使它不是一个构造函数?
Fel*_*ing 40
一点背景:
ECMAScript 6+区分可调用(可以不调用new)和可构造(可以调用new)函数:
class语法创建的函数不可调用.Function构造函数)都是可调用和可构造的.关于 Function.prototype
Function.prototype是一个不可构造的所谓内置函数 .从规格:
[[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;
}
Run Code Online (Sandbox Code Playgroud)
但是,这种方法不是故障保护,因为函数可能有副作用,因此在调用之后f,您不知道环境所处的状态.
此外,这只会告诉你一个功能是否可以被称为构造函数,而不是如果打算被称为构造函数.为此,您必须查看文档或函数的实现.
注意:永远不应该有理由在生产环境中使用像这样的测试.是否应该调用函数new应该可以从其文档中辨别出来.
当我创建一个函数时,如何使它不是一个构造函数?
要创建一个真正无法构造的函数,可以使用箭头函数:
var f = () => console.log('no constructable');
Run Code Online (Sandbox Code Playgroud)
根据定义,箭头函数不可构造.或者,您可以将函数定义为对象或类的方法.
否则你可以new通过检查它的this值来检查函数是否被调用(或类似的东西),如果是,则抛出错误:
function foo() {
if (this instanceof foo) {
throw new Error("Don't call 'foo' with new");
}
}
Run Code Online (Sandbox Code Playgroud)
当然,由于还有其他方法来设定价值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()');Run Code Online (Sandbox Code Playgroud)
Art*_*yer 12
您正在寻找函数是否具有[[Construct]]内部方法.内部方法IsConstructor详细介绍了以下步骤:
IsConstructor(argument)Run Code Online (Sandbox Code Playgroud)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(new.target在js中),它可以用于Reflect.construct:
function is_constructor(f) {
try {
Reflect.construct(String, [], f);
} catch (e) {
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
(我本来可以使用任何东西,比如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)
Run Code Online (Sandbox Code Playgroud)
<注意>
我发现它没有用的唯一价值是Symbol,虽然在Firefox中new Symbol抛出了,但是.这是技术上正确的答案,因为确实有一个内部方法(这意味着它也可以被继承),但使用或者是特殊的套管用于抛出一个错误(所以,是一个构造函数,该错误信息是错误的,它只是可以不能用作一个.)你可以添加到顶部.TypeError: Symbol is not a constructoris_constructor(Symbol) === trueSymbol [[Construct]]newsuperSymbolSymbolif (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.
Run Code Online (Sandbox Code Playgroud)
因此,作为构造函数的功能的意图不能像这样(直到Symbol.is_constructor添加某些东西或其他标志).
</注释>
Dmi*_*eev 10
有一种快速简便的方法可以确定函数是否可以实例化,而不必使用try-catch语句(无法通过v8优化)
function isConstructor(obj) {
return !!obj.prototype && !!obj.prototype.constructor.name;
}
Run Code Online (Sandbox Code Playgroud)
有一个警告,即:在定义中命名的函数仍然会产生名称属性,从而通过此检查,因此在依赖函数构造函数的测试时需要谨慎.
在下面的示例中,该函数不是匿名函数,但实际上称为"myFunc".它的原型可以像任何JS类一样扩展.
let myFunc = function () {};
Run Code Online (Sandbox Code Playgroud)
:)
小智 7
使用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
}
}
Run Code Online (Sandbox Code Playgroud)
如果传递的项不是对象,则Proxy构造函数将引发错误.如果它不是可构造的对象,则new抛出错误.但是如果它是一个可构造的对象,那么它返回handler对象而不调用它的构造函数,然后它就不会被引入true.
正如您所料,Symbol仍然被认为是构造函数.那是因为它是,并且实现仅在[[Construct]]调用时抛出错误.这可能是任何用户定义的函数在new.target存在时抛出错误的情况,因此将其作为附加检查特别清除它似乎是正确的,但如果您发现它有用,请随意这样做.
如果函数是构造函数,那么它将有一个“原型”成员,而“原型”成员又具有一个等于函数本身的“构造函数”成员。
function isConstructor(func) {
return typeof func === 'function' && !!func.prototype && func.prototype.constructor === func;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12706 次 |
| 最近记录: |