因为现有的答案从ES5环境中解决了这个问题我认为值得从ES2015中给出答案+观点;最初的问题没有具体说明,今天很多人都没有更长的时间需要转换类,这稍微改变了情况。
我特别想指出是可以明确回答“这个价值观能被构建吗?”诚然,这通常没有用处独立;如果你需要知道,同样的基本问题仍然存在如果可以调用值。
什么东西是可以建造的吗?
首先,我认为我们需要澄清一些术语,因为我们需要询问value是一个构造函数可能意味着不止一件事:
- 从字面上看,这个值有[[construct]]槽吗?如果是,那就是可施工。如果没有,则它是不可构造的。
- 是这个功能吗有意的要建造吗?我们可以产生一些负面影响:功能不能建造的,不是打算建造的。但我们不能也可以说(不采用启发式检查)可建造的不是意味着用作构造函数。
2无法回答的是,使用功能
关键字单独一个函数既可构造又可调用,但通常需要这样的函数仅用于其中一个目的。正如其他人提到的,2也是一个可疑的问题-这类似于问“作者在写作时是怎么想的写了这个?“我认为人工智能还没有出现:)虽然在一个完美的世界里,也许所有的作者都会保留施工人员的PascalCase(见balupton’s是常规类
功能),in实践中,这种测试经常会出现假阳性/假阴性。
关于这个问题的第一个版本,是的,我们可以知道函数是否可施工。显然,要做的是尝试构建它。但这实际上是不可接受的,因为我们不知道这样做是否会有好处效果-我们似乎对函数的性质一无所知,因为如果我们知道,就不需要进行此检查)。幸运的是,有一种方法可以构建没有的构造函数真正地构建它:
const isConstructable=fn=>{尝试{新代理(fn,{construct:()=>({})});返回true;}捕获(错误){返回false;}};
这个建造
代理处理程序可以覆盖代理值的[[construct]],但它不能使不可构造的值可构造。所以我们可以“嘲笑”实例化“输入以测试是否失败。请注意陷阱必须返回一个对象。
isConstructable(类{});//真的isConstructable(类{}.bind());//真的是可构造的(函数(){});//真的isConstructable(函数(){}.bind());//真的可构造(()=>{});//假isConstructable((()=>{}).bind());//假isConstructable(异步()=>{});//假isConstructable(异步函数(){});//假isConstructable(函数*(){});//假可构造({foo(){}}.foo);//假可构造(URL);//真的
注意,箭头函数、异步函数、生成器和方法不是“遗留”函数声明和表达式具有双重责任。这些函数没有[[construct]]槽(我想没有多少人意识到“速记方法”语法是有作用的——它不仅仅是糖)。
综上所述,如果你的问题真的是“这是可构造的”,那么上面的结论是肯定的。不幸的是,其他的都不会。
有什么可以调用的吗?
我们必须再次澄清这个问题,因为如果我们非常直白,那么以下测试实际上有效*:
const isCallable=fn=>类型fn===“函数”;
这是因为ES当前不允许您创建没有[[调用]]槽(好吧,绑定函数没有直接的槽,但它们代理到函数)。
这可能看起来不真实,因为构造函数是用类创建的如果你试图调用它们而不是构造它们,语法就会抛出。然而,他们是可调用的-只是它们的[[调用]]槽被定义为一个函数真是太棒了!Oy公司。
我们可以通过将第一个函数转换为其镜像来证明这一点。
//仅用于演示,此功能无效:常量可调用=fn=>{尝试{新代理(fn,{apply:()=>未定义})();返回true;}捕获(错误){返回false;}};isCallable(()=>{});//真的可调用(函数(){});//真的isCallable(类{});//。。。真的!
这样的函数没有帮助,但我想将这些结果显示给关注问题的本质。我们无法轻松检查功能是“新的”,即答案不是根据“缺少所谓“永无止境”的方式是根据“缺乏构造”来建模的。我们感兴趣的知识被埋藏在一种方法中,我们只能通过评估才能观察到它,所以我们所能做的就是使用启发式检查作为我们真正想知道的东西的代理。
启发性选项
我们可以从缩小模棱两可的案例开始。任何功能是不constructable在两种意义上都是明确可调用的:如果类型fn===“函数”
但是isConstructable(fn)===假
,我们有一个只调用函数,如箭头、生成器或方法。
所以这四个有趣的案例是类{}
和函数(){}
加上界限两者的形式。我们能说的一切都是可以调用的。请注意当前的答案提到了绑定函数,但这些函数引入了重要的任何启发式检查的问题。
正如balupton所指出的“caller”属性可以作为函数创建方式的指示器。绑定函数外来对象将不具有此自身属性,即使它包装的功能。该属性将通过继承而存在功能.原型
,但对于类构造函数也是如此。
同样,BFEO的toString通常以“函数”开头,即使绑定函数是用类创建的。现在,一种探测BFEO本身的启发式方法会去看看他们的名字是否开始“绑定”,但不幸的是这是一个死亡结束;它仍然没有告诉我们什么被绑定了&这对我们来说是不透明的。
然而,如果toString确实返回“class”(对于DOM来说,这不是真的构造函数),这是一个非常可靠的信号,表明它不可调用。
那么我们能做的最好的事情就是这样:
常量为DefinelyCallable=fn=>类型fn===“函数”&&!可建造(fn);isDefinelyCallable(类{});//假isDefinitelyCallable(类{}.bind());//假isDefinelyCallable(函数(){});//false<--可调用isDefinitionCallable(函数(){}.bind());//false<--可调用isDefinelyCallable(()=>{});//真的isDefinelyCallable(()=>{}).bind());//真的isDefinelyCallable(异步()=>{});//真的isDefinelyCallable(异步函数(){});//真的isDefinelyCallable(函数*(){});//真的isDefinelyCallable({foo(){}}.foo);//真的isDefinitelyCallable(URL);//假常数isProbablyNotCallable=fn=>类型fn!=='函数'||fn.toString().startsWith(“类”)||布尔值(fn.原型&&!Object.getOwnPropertyDescriptor(fn,“prototype”).writeable//或您的最爱);isProbablyNotCallable(类{});//真的isProbablyNotCallable(类{}.bind());//false<--不可调用isProbablyNotCallable(函数(){});//假isProbablyNotCallable(函数(){}.bind());//假isProbablyNotCallable(()=>{});//假isProbablyNotCallable(()=>{}).bind());//假isProbablyNotCallable(异步()=>{});//假isProbablyNotCallable(异步函数(){});//假isProbablyNotCallable(函数*(){});//假isProbablyNotCallable({foo(){}}.foo);//假isProbablyNotCallable(URL);//真的
带箭头的案例指出了我们不太喜欢的答案。
在isProbablyNotCallable函数中,条件的最后一部分可以是替换为其他答案中的其他检查;我选择了Miguel Mota在这里,因为它碰巧也适用于(大多数?)DOM构造函数,甚至那些定义的在引入ES类之前。但这并不重要——每一种可能支票有缺点,没有魔术组合。
据我所知,以上描述了在当代ES。它没有解决ES5和更早版本的特定需求,尽管在ES5和更早的版本中,这两个问题的答案总是“true”表示任何函数。
未来
有一个关于本地测试的建议,该测试将成为[[FunctionKind]]插槽在揭示函数是否是用班
:
https://github.com/caitp/TC39-Proposals/blob/master/TC39-reflect-isconstructor-iscallable.md
如果这项提议或类似的提议取得进展,我们将获得解决方案这个问题具体到什么时候班
至少。
*忽略附录B[[IsHTMLDDA]]案例。