问题描述
给定一个函数:
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 及更高版本中现在是可能的.请参阅丹尼尔·韦纳 (Daniel Weiner) 的回答.
我不认为你想要的是可能的 [在 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 3rd edition spec,调用new x()
时的步骤基本上是:
Looking at the ECMAScript 3rd edition spec, the steps taken when new x()
is called are essentially:
- 创建一个新对象
- 将其内部的 [[Prototype]] 属性赋值给
x
的原型属性 - 正常调用
x
,将新对象作为this
传递给它 - 如果调用
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
的预先存在的实例无法区分as this
当将 x
作为函数调用时,除非你为 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.
(*) instance of"是一个不准确的术语,但足够接近,比通过调用 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"
这篇关于如何检测函数是否被称为构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!