如何使用调用或应用程序调用JavaScript构造函数?

我怎么能概括下面的函数拿N个参数? (使用电话或申请?)

有没有一个程序化的方法来将参数应用到“新”? 我不希望构造函数被视为一个普通的函数。

/** * 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"); } } 

qUnittesting:

 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"); }); 

尝试这个:

 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 (ie 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; } } 

这是你如何做到的:

 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]); 

呼叫稍微简单一些

 function callConstructor(constructor) { var factoryFunction = constructor.bind.apply(constructor, arguments); return new factoryFunction(); } var d = callConstructor(Date, 2008, 10, 8, 00, 16, 34, 254); 

您可以使用其中任何一种来创build工厂function:

 var dateFactory = applyToConstructor.bind(null, Date) var d = dateFactory([2008, 10, 8, 00, 16, 34, 254]); 

要么

 var dateFactory = callConstructor.bind(null, Date) var d = dateFactory(2008, 10, 8, 00, 16, 34, 254); 

它可以和任何构造函数一起工作,不仅可以作为函数(如Date)的内build函数或构造函数。

但它确实需要Ecmascript 5 .bind函数。 垫片可能无法正常工作。

一种不同的方法,更多的是其他答案的风格是创build一个内置的newfunction版本。 这不适用于所有内build(如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"); 

现在,当然你可以像平常那样在neu上执行.apply.apply.bind

例如:

 var personFactory = neu.bind(null, Person); var d = personFactory("Harry", "Potter"); 

我觉得我提供的第一个解决scheme是更好的,因为它不依赖于你正确地复制一个内置的语义,它可以与内置的正确工作。

这个function在所有情况下都是相同的。 这可能会比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() + ')'); } 

更新:一旦ES6支持广泛,你可以写这个:

 function applyConstructor(ctor, args) { return new ctor(...args); } 

…但是你不需要,因为标准库函数Reflect.construct()完全符合你的要求!

另一种方法,需要修改被调用的实际构造函数,但比使用eval()或在构build链中引入一个新的虚拟函数似乎更清洁…保持你的conthunktorfunction

 function conthunktor(Constructor) { // Call the constructor return Constructor.apply(null, Array.prototype.slice.call(arguments, 1)); } 

并修改正在调用的构造函数…

 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... } 

所以你可以尝试:

 var myInstance = conthunktor(MyConstructor, 1, 2, 3); var sum = myInstance.a + myInstance.b + myInstance.c; // sum is 6 

如果Object.create不可用,使用临时构造函数似乎是最好的解决scheme。

如果Object.create可用,那么使用它是一个更好的select。 在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; } 

(显然, args参数是要应用的参数列表。)

我有一段代码最初使用eval来读取由另一个工具创build的数据。 (是的, eval是邪恶的。)这将实例化一个数百到数千个元素的树。 基本上,JavaScript引擎负责parsing和执行一堆new ...(...)expression式。 我转换我的系统来parsingJSON结构,这意味着我必须让我的代码确定哪个构造函数调用树中的每种types的对象。 当我在testing套件中运行新代码时,我惊讶地看到相对于eval版本的一个显着的减速。

  1. testing套件与eval版本: 1秒。
  2. testing套件与JSON版本,使用临时构造函数: 5秒。
  3. testing套件与JSON版本,使用Object.create1秒。

testing套件创build多个树。 我计算了我的applytoConstructor函数在testing套件运行时调用了大约125,000次。

在ECMAScript 6中,可以使用spread运算符将具有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 

这种情况有一个可重复的解决scheme。 对于每个要使用apply或call方法调用的Class,必须先调用convertToAllowApply('classNameInString'); 该类必须在同一个Scoope o全局scoope(我不尝试发送ns.className例如…)

有这样的代码:

 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);