How To Construct Javascript Object (using 'apply')?
Solution 1:
In ES5, you can do it via bind
.
function construct(constructor, args) {
return new (constructor.bind.apply(constructor, [null].concat(args)));
}
which works because bind
still uses the [[Construct]] abstract operator when the bound function appears to the right of new
per http://es5.github.com/#x15.3.4.5.2 which says
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:
- 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.
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;
}
Solution 2:
I may end up with this simplified version of Mike's triangle-of-hackery:
functionapplyCtor2(ctor, args) {
switch (args.length) {
case0: returnnewctor();
case1: returnnewctor(args[0]);
case2: returnnewctor(args[0], args[1]);
// add more cases if you like
}
var jsStr = "new ctor(args[0]";
for (var i=1; i<ar.length; i++) jsStr += ",args[" + i + "]";
jsStr += ")";
returneval(jsStr);
}
I'm not using 'apply' here, but I don't miss it. ;-) Any comments?
Post a Comment for "How To Construct Javascript Object (using 'apply')?"