如何使用 call 或 apply 调用 javascript 构造函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3362471/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I call a javascript constructor using call or apply?
提问by fadedbee
How could I generalise the function below to take N arguments? (Using call or apply?)
我如何概括下面的函数来接受 N 个参数?(使用电话还是申请?)
Is there a programmatic way to apply arguments to 'new'? I don't want the constructor to be treated like a plain function.
是否有编程方式将参数应用于“新”?我不希望构造函数被视为普通函数。
/**
* This higher level function takes a constructor and arguments
* and returns a function, which when called will return the
* lazily constructed value.
*
* All the arguments, except the first are pased to the constructor.
*
* @param {Function} constructor
*/
function conthunktor(Constructor) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
console.log(args);
if (args.length === 0) {
return new Constructor();
}
if (args.length === 1) {
return new Constructor(args[0]);
}
if (args.length === 2) {
return new Constructor(args[0], args[1]);
}
if (args.length === 3) {
return new Constructor(args[0], args[1], args[2]);
}
throw("too many arguments");
}
}
qUnit test:
qUnit测试:
test("conthunktorTest", function() {
function MyConstructor(arg0, arg1) {
this.arg0 = arg0;
this.arg1 = arg1;
}
MyConstructor.prototype.toString = function() {
return this.arg0 + " " + this.arg1;
}
var thunk = conthunktor(MyConstructor, "hello", "world");
var my_object = thunk();
deepEqual(my_object.toString(), "hello world");
});
采纳答案by James
Try this:
尝试这个:
function conthunktor(Constructor) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var Temp = function(){}, // temporary constructor
inst, ret; // other vars
// Give the Temp constructor the Constructor's prototype
Temp.prototype = Constructor.prototype;
// Create a new instance
inst = new Temp;
// Call the original Constructor with the temp
// instance as its context (i.e. its 'this' value)
ret = Constructor.apply(inst, args);
// If an object has been returned then return it otherwise
// return the original instance.
// (consistent with behaviour of the new operator)
return Object(ret) === ret ? ret : inst;
}
}
回答by kybernetikos
This is how you do it:
这是你如何做到的:
function applyToConstructor(constructor, argArray) {
var args = [null].concat(argArray);
var factoryFunction = constructor.bind.apply(constructor, args);
return new factoryFunction();
}
var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]);
Call is slightly easier
通话稍微容易一些
function callConstructor(constructor) {
var factoryFunction = constructor.bind.apply(constructor, arguments);
return new factoryFunction();
}
var d = callConstructor(Date, 2008, 10, 8, 00, 16, 34, 254);
You can use either of these to create factory functions:
您可以使用其中任何一个来创建工厂函数:
var dateFactory = applyToConstructor.bind(null, Date)
var d = dateFactory([2008, 10, 8, 00, 16, 34, 254]);
or
或者
var dateFactory = callConstructor.bind(null, Date)
var d = dateFactory(2008, 10, 8, 00, 16, 34, 254);
It will work with any constructor, not just built-ins or constructors that can double as functions (like Date).
它适用于任何构造函数,而不仅仅是内置函数或可以兼作函数的构造函数(如 Date)。
However it does require the Ecmascript 5 .bind function. Shims will probably not work correctly.
但是它确实需要 Ecmascript 5 .bind 函数。垫片可能无法正常工作。
A different approach, more in the style of some of the other answers is to create a function version of the built in new. This will not work on all builtins (like Date).
一种不同的方法,更符合其他一些答案的风格,是创建内置new. 这不适用于所有内置函数(如 Date)。
function neu(constructor) {
// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2
var instance = Object.create(constructor.prototype);
var result = constructor.apply(instance, Array.prototype.slice.call(arguments, 1));
// The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.
return (result !== null && typeof result === 'object') ? result : instance;
}
function Person(first, last) {this.first = first;this.last = last};
Person.prototype.hi = function(){console.log(this.first, this.last);};
var p = neu(Person, "Neo", "Anderson");
And now, of course you can do .applyor .callor .bindon neuas normal.
而现在,当然你可以做.apply或.call或.bind在neu正常进行。
For example:
例如:
var personFactory = neu.bind(null, Person);
var d = personFactory("Harry", "Potter");
I feel that the first solution I give is better though, as it doesn't depend on you correctly replicating the semantics of a builtin and it works correctly with builtins.
我觉得我给出的第一个解决方案更好,因为它不依赖于您正确复制内置函数的语义,并且它与内置函数一起正常工作。
回答by Jason Orendorff
This function is identical to newin all cases. It will probably be significantly slower than 999's answer, though, so use it only if you really need it.
此功能new在所有情况下都相同。不过,它可能会比 999 的答案慢得多,因此只有在您确实需要时才使用它。
function applyConstructor(ctor, args) {
var a = [];
for (var i = 0; i < args.length; i++)
a[i] = 'args[' + i + ']';
return eval('new ctor(' + a.join() + ')');
}
UPDATE:Once ES6 support is widespread, you'll be able to write this:
更新:一旦 ES6 支持广泛使用,你就可以这样写:
function applyConstructor(ctor, args) {
return new ctor(...args);
}
...but you won't need to, because the standard library function Reflect.construct()does exactly what you're looking for!
...但您不需要,因为标准库函数Reflect.construct()正是您正在寻找的!
回答by Fuerteflojo
Another approach, which requires to modify the actual constructor being called, but seems cleaner to me than using eval() or introducing a new dummy function in the construction chain... Keep your conthunktor function like
另一种方法,它需要修改被调用的实际构造函数,但对我来说似乎比使用 eval() 或在构造链中引入一个新的虚拟函数更干净......保持你的 conthunktor 函数像
function conthunktor(Constructor) {
// Call the constructor
return Constructor.apply(null, Array.prototype.slice.call(arguments, 1));
}
And modify the constructors being called...
并修改被调用的构造函数......
function MyConstructor(a, b, c) {
if(!(this instanceof MyConstructor)) {
return new MyConstructor(a, b, c);
}
this.a = a;
this.b = b;
this.c = c;
// The rest of your constructor...
}
So you can try:
所以你可以试试:
var myInstance = conthunktor(MyConstructor, 1, 2, 3);
var sum = myInstance.a + myInstance.b + myInstance.c; // sum is 6
回答by Louis
Using a temporary constructor seems to be the best solution if Object.createis not available.
如果Object.create不可用,使用临时构造函数似乎是最好的解决方案。
If Object.createis available, then using it is a much better option. On Node.js, using Object.createresults in much faster code.Here's an example of how Object.createcan be used:
如果Object.create可用,那么使用它是一个更好的选择。在 Node.js 上,使用会Object.create产生更快的代码。以下是如何Object.create使用的示例:
function applyToConstructor(ctor, args) {
var new_obj = Object.create(ctor.prototype);
var ctor_ret = ctor.apply(new_obj, args);
// Some constructors return a value; make sure to use it!
return ctor_ret !== undefined ? ctor_ret: new_obj;
}
(Obviously, the argsargument is a list of arguments to apply.)
(显然,args参数是要应用的参数列表。)
I had a piece of code that originally used evalto read a piece of data created by another tool. (Yes, evalis evil.) This would instantiate a tree of hundreds to thousands of elements. Basically, the JavaScript engine was responsible for parsing and executing a bunch of new ...(...)expressions. I converted my system to parse a JSON structure, which means I have to have my code determine which constructor to call for each type of object in the tree. When I ran the new code in my test suite, I was surprised to see a dramatic slow down relative to the evalversion.
我有一段代码,最初用于eval读取由另一个工具创建的一段数据。(是的,eval是邪恶的。)这将实例化一棵包含数百到数千个元素的树。基本上,JavaScript 引擎负责解析和执行一堆new ...(...)表达式。我将系统转换为解析 JSON 结构,这意味着我必须让我的代码确定为树中的每种类型的对象调用哪个构造函数。当我在我的测试套件中运行新代码时,我惊讶地看到相对于eval版本的显着放缓。
- Test suite with
evalversion: 1 second. - Test suite with JSON version, using temporary constructor: 5 seconds.
- Test suite with JSON version, using
Object.create: 1 second.
eval版本测试套件:1 秒。- 带有 JSON 版本的测试套件,使用临时构造函数:5 秒。
- 带有 JSON 版本的测试套件,使用
Object.create:1 秒。
The test suite creates multiple trees. I calculated my applytoConstructorfunction was called about 125,000 times when the test suite is run.
测试套件创建多个树。我计算出我的applytoConstructor函数在测试套件运行时被调用了大约 125,000 次。
回答by Ghasem Kiani
In ECMAScript 6, you can use the spread operator to apply a constructor with the new keyword to an array of arguments:
在 ECMAScript 6 中,您可以使用扩展运算符将带有 new 关键字的构造函数应用于参数数组:
var dateFields = [2014, 09, 20, 19, 31, 59, 999];
var date = new Date(...dateFields);
console.log(date); // Date 2014-10-20T15:01:59.999Z
回答by danyg
There is a rehusable solution for this case. For every Class to you wish to call with apply or call method, you must call before to convertToAllowApply('classNameInString'); the Class must be in the same Scoope o global scoope (I don't try sending ns.className for example...)
对于这种情况,有一个可回收的解决方案。对于您希望使用 apply 或 call 方法调用的每个类,您必须先调用 convertToAllowApply('classNameInString'); Class 必须在同一个 Scoope o global scoope 中(例如,我不会尝试发送 ns.className...)
There is the code:
有代码:
function convertToAllowApply(kName){
var n = '\n', t = '\t';
var scrit =
'var oldKlass = ' + kName + ';' + n +
kName + '.prototype.__Creates__ = oldKlass;' + n +
kName + ' = function(){' + n +
t + 'if(!(this instanceof ' + kName + ')){'+ n +
t + t + 'obj = new ' + kName + ';'+ n +
t + t + kName + '.prototype.__Creates__.apply(obj, arguments);'+ n +
t + t + 'return obj;' + n +
t + '}' + n +
'}' + n +
kName + '.prototype = oldKlass.prototype;';
var convert = new Function(scrit);
convert();
}
// USE CASE:
myKlass = function(){
this.data = Array.prototype.slice.call(arguments,0);
console.log('this: ', this);
}
myKlass.prototype.prop = 'myName is myKlass';
myKlass.prototype.method = function(){
console.log(this);
}
convertToAllowApply('myKlass');
var t1 = myKlass.apply(null, [1,2,3]);
console.log('t1 is: ', t1);

