如何检测一个函数是否被称为构造函数? [英] How to detect if a function is called as constructor?
问题描述
给定一个函数:
function x(arg) { return 30; }
您可以通过两种方式进行调用:
You can call it two ways:
result = x(4);
result = new x(4);
第一个返回30,第二个返回一个对象。
The first returns 30, the second returns an object.
如何检测函数在函数本身中被调用的方式?
How can you detect which way the function was called inside the function itself?
无论你的解决方案是什么,还有以下调用:
Whatever your solution is, it must work with the following invocation as well:
var Z = new x();
Z.lolol = x;
Z.lolol();
所有解决方案目前认为 Z.lolol()
正在调用它作为构造函数。
All the solutions currently think the Z.lolol()
is calling it as a constructor.
推荐答案
注意:现在可以在ES2015及更高版本。请参阅丹尼尔·韦纳的答案。
NOTE: This is now possible in ES2015 and later. See Daniel Weiner's answer.
我'想想你想要的是可能的[在ES2015之前]。
I don't think what you want is possible [prior to ES2015]. There simply isn't enough information available within the function to make a reliable inference.
查看ECMAScript第三版规范,所采取的步骤, new x()
本质上是:
Looking at the ECMAScript 3rd edition spec, the steps taken when new x()
is called are essentially:
- 创建一个新对象
- 将其内部[[Prototype]]属性分配给
x
的原型属性 - 调用
x c>
- 如果调用
x
返回一个对象,返回它,否则返回新对象
- Create a new object
- Assign its internal [[Prototype]] property to the prototype property of
x
- Call
x
as normal, passing it the new object asthis
- If the call to
x
returned an object, return it, otherwise return the new object
被调用可用于执行代码,所以唯一可以在 x
里测试的是 this
值,这是这里所有的答案都在做。正如你所看到的,当调用 x
作为构造函数时,一个新的* x
实例,当调用 x
时, x 的现有实例传递为
this
除非,因为它是由 x
创建的每个新对象分配一个属性:
Nothing useful about how the function was called is made available to the executing code, so the only thing it's possible to test inside x
is the this
value, which is what all the answers here are doing. As you've observed, a new instance of* x
when calling x
as a constructor is indistinguishable from a pre-existing instance of x
passed as this
when calling x
as a function, unless you assign a property to every new object created by x
as it is constructed:
function x(y) {
var isConstructor = false;
if (this instanceof x // <- You could use arguments.callee instead of x here,
// except in in EcmaScript 5 strict mode.
&& !this.__previouslyConstructedByX) {
isConstructor = true;
this.__previouslyConstructedByX = true;
}
alert(isConstructor);
}
显然这不是理想的,因为你现在有一个额外的无用的属性由 x
构造的对象可以被覆盖,但我认为这是最好的。
Obviously this is not ideal, since you now have an extra useless property on every object constructed by x
that could be overwritten, but I think it's the best you can do.
(*)实例是一个不准确的术语,但足够接近,并且比通过调用 x
作为构造函数创建的对象更简洁
(*) "instance of" is an inaccurate term but is close enough, and more concise than "object that has been created by calling x
as a constructor"
这篇关于如何检测一个函数是否被称为构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!