在JavaScript中,我想创建一个对象实例(通过new操作符),但将任意数量的参数传递给构造函数。这可能吗?
我想做的是这样的(但下面的代码不起作用):
function Something(){
// init stuff
}
function createSomething(){
return new Something.apply(null, arguments);
}
var s = createSomething(a,b,c); // 's' is an instance of Something
这个问题的答案
从这里的响应可以清楚地看出,没有内置的方法可以使用new操作符调用.apply()。然而,人们对这个问题提出了许多非常有趣的解决方案。
我更喜欢的解决方案是来自Matthew Crumley的这个(我修改了它来传递arguments属性):
var createSomething = (function() {
function F(args) {
return Something.apply(this, args);
}
F.prototype = Something.prototype;
return function() {
return new F(arguments);
}
})();
这是我的createssomething版本:
function createSomething() {
var obj = {};
obj = Something.apply(obj, arguments) || obj;
obj.__proto__ = Something.prototype; //Object.setPrototypeOf(obj, Something.prototype);
return o;
}
在此基础上,我尝试模拟JavaScript的new关键字:
//JavaScript 'new' keyword simulation
function new2() {
var obj = {}, args = Array.prototype.slice.call(arguments), fn = args.shift();
obj = fn.apply(obj, args) || obj;
Object.setPrototypeOf(obj, fn.prototype); //or: obj.__proto__ = fn.prototype;
return obj;
}
我对它进行了测试,它似乎在所有场景下都能很好地工作。它也适用于原生构造函数,如Date。下面是一些测试:
//test
new2(Something);
new2(Something, 1, 2);
new2(Date); //"Tue May 13 2014 01:01:09 GMT-0700" == new Date()
new2(Array); //[] == new Array()
new2(Array, 3); //[undefined × 3] == new Array(3)
new2(Object); //Object {} == new Object()
new2(Object, 2); //Number {} == new Object(2)
new2(Object, "s"); //String {0: "s", length: 1} == new Object("s")
new2(Object, true); //Boolean {} == new Object(true)
感谢这里的帖子,我这样使用它:
SomeClass = function(arg1, arg2) {
// ...
}
ReflectUtil.newInstance('SomeClass', 5, 7);
和实现:
/**
* @param strClass:
* class name
* @param optionals:
* constructor arguments
*/
ReflectUtil.newInstance = function(strClass) {
var args = Array.prototype.slice.call(arguments, 1);
var clsClass = eval(strClass);
function F() {
return clsClass.apply(this, args);
}
F.prototype = clsClass.prototype;
return new F();
};
虽然其他方法是可行的,但它们过于复杂。在Clojure中,您通常创建一个实例化类型/记录的函数,并使用该函数作为实例化的机制。翻译成JavaScript:
function Person(surname, name){
this.surname = surname;
this.name = name;
}
function person(surname, name){
return new Person(surname, name);
}
通过采用这种方法,可以避免使用上面描述的new except。当然,这个函数与apply或任何其他函数式编程特性一起工作都没有问题。
var doe = _.partial(person, "Doe");
var john = doe("John");
var jane = doe("Jane");
通过使用这种方法,您的所有类型构造函数(例如Person)都是普通的、不做任何事情的构造函数。您只需传入参数并将它们分配给同名的属性。麻烦的细节放在构造函数中(例如person)。
创建这些额外的构造函数没有什么麻烦,因为它们是一种很好的实践。它们很方便,因为它们允许您潜在地拥有几个具有不同细微差别的构造函数。
假设你有一个Items构造函数,它吸收了你给它的所有参数:
function Items () {
this.elems = [].slice.call(arguments);
}
Items.prototype.sum = function () {
return this.elems.reduce(function (sum, x) { return sum + x }, 0);
};
你可以用Object.create()创建一个实例,然后用.apply()创建该实例:
var items = Object.create(Items.prototype);
Items.apply(items, [ 1, 2, 3, 4 ]);
console.log(items.sum());
当运行时输出10,因为1 + 2 + 3 + 4 == 10:
$ node t.js
10