如何构造JavaScript对象(使用'apply')? [英] How to construct JavaScript object (using 'apply')?
问题描述
我正在寻找一种基于(a)构造函数的名称和(b)包含参数的数组来构造任意JavaScript对象的方法。我在stackoverflow的另一个线程中发现了这个函数(由Matthew Crumley?):
I'm looking for a way to construct arbitrary JavaScript objects based on (a) the name of the constructor, and (b) an array containing the arguments. I found this function (by Matthew Crumley ?) in an other thread on stackoverflow:
function construct(constructor, args) {
function F() { return constructor.apply(this, args); }
F.prototype = constructor.prototype;
return new F();
}
这适用于用JavaScript编写的构造函数,我尝试构造(Date,[...])。我还不知道如果有更多的本机构造函数,这个函数不能处理。
This works well with constructors written in JavaScript, but it fails with a TypeError if I try construct(Date, [...]). I don't know yet if there are more native constructors that this function can't handle. My questions are then ...
- 更新版本的JavaScript(ECMAScript 5)中是否有函数可以解决我的问题? li>
- 如果没有,是否有一些方法,我可以检查的问题的构造函数,看看是否可以使用上述函数? (如果不能,我可能需要使用eval(new+ cname +(+ arglist +))。)
/ Jon
推荐答案
在ES5中,您可以通过 bind
。
In ES5, you can do it via bind
.
function construct(constructor, args) {
return new (constructor.bind.apply(constructor, [null].concat(args)));
}
这是因为 bind
new
每个 http://es5.github.com/#x15.3.4.5.2 说
15.3.4.5.2 [[Construct]]
当一个函数对象的[[Construct]]内部方法,使用参数ExtraArgs的列表调用
bind
函数,执行以下步骤:
15.3.4.5.2 [[Construct]]
When the [[Construct]] internal method of a function object, F that was created using the
bind
function is called with a list of arguments ExtraArgs, the following steps are taken:
- 让target为F的[[TargetFunction]]内部属性的值。
- 如果目标没有[[Construct]]内部方法,则抛出TypeError异常。 li>
- 让boundArgs是F的[[BoundArgs]]内部属性的值。
- 让args是一个包含与列表boundArgs相同的值的新列表
- 返回调用目标的[[Construct]]内部方法的结果,提供args作为参数。/ li>
- Let target be the value of F’s [[TargetFunction]] internal property.
- If target has no [[Construct]] internal method, a TypeError exception is thrown.
- Let boundArgs be the value of F’s [[BoundArgs]] internal property.
- Let args be a new list containing the same values as the list boundArgs in the same order followed by the same values as the list ExtraArgs in the same order.
- Return the result of calling the [[Construct]] internal method of target providing args as the arguments.
但是大多数实现 Function.prototype.bind
试图将语言特性后端到ES3实现没有正确地处理用作构造函数的绑定函数,所以如果你不确定你的代码是在真正的ES5实现上运行,那么你必须回到三角形的hackery:
but most implementations of Function.prototype.bind
that attempt to back-port the language feature onto ES3 implementations do not correctly handle bound functions used as a constructor, so if you're not sure your code is running on a real ES5 implementation then you have to fall back to the triangle of hackery:
function applyCtor(ctor, args) {
// Triangle of hackery which handles host object constructors and intrinsics.
// Warning: The goggles! They do nothing!
switch (args.length) {
case 0: return new ctor;
case 1: return new ctor(args[0]);
case 2: return new ctor(args[0], args[1]);
case 3: return new ctor(args[0], args[1], args[2]);
case 4: return new ctor(args[0], args[1], args[2], args[3]);
case 5: return new ctor(args[0], args[1], args[2], args[3], args[4]);
case 6: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
case 7: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
case 9: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
case 10: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
case 11: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]);
case 12: return new ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]);
}
// End triangle of hackery
// Create a throwaway subclass of ctor whose constructor does nothing.
function TemporarySubclass() {}
TemporarySubclass.prototype = ctor.prototype;
var instance = new TemporarySubclass();
instance.constructor = ctor; // Patch constructor property
// Run the constructor. This assumes that [[Call]] internal method is the same as
// [[Construct]]. It might work with some builtins/host objects where "new` would not.
var returnValue = ctor.apply(instance, args);
// If the constructor returned a non-primitive value, return it instead.
switch (typeof returnValue) {
case 'object':
// If ctor is Array, it reaches here so we don't use broken Array subclass.
// Ditto for Date.
if (returnValue) { return returnValue; }
break;
case 'function':
return returnValue;
}
// Return the constructed instance.
return instance;
}
这篇关于如何构造JavaScript对象(使用'apply')?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!