在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);
}
})();
感谢这里的帖子,我这样使用它:
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)。
创建这些额外的构造函数没有什么麻烦,因为它们是一种很好的实践。它们很方便,因为它们允许您潜在地拥有几个具有不同细微差别的构造函数。
function FooFactory() {
var prototype, F = function(){};
function Foo() {
var args = Array.prototype.slice.call(arguments),
i;
for (i = 0, this.args = {}; i < args.length; i +=1) {
this.args[i] = args[i];
}
this.bar = 'baz';
this.print();
return this;
}
prototype = Foo.prototype;
prototype.print = function () {
console.log(this.bar);
};
F.prototype = prototype;
return Foo.apply(new F(), Array.prototype.slice.call(arguments));
}
var foo = FooFactory('a', 'b', 'c', 'd', {}, function (){});
console.log('foo:',foo);
foo.print();
我遇到了这个问题,我是这样解决的:
function instantiate(ctor) {
switch (arguments.length) {
case 1: return new ctor();
case 2: return new ctor(arguments[1]);
case 3: return new ctor(arguments[1], arguments[2]);
case 4: return new ctor(arguments[1], arguments[2], arguments[3]);
//...
default: throw new Error('instantiate: too many parameters');
}
}
function Thing(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}
var thing = instantiate(Thing, 'abc', 123, {x:5});
是的,这有点丑,但它解决了问题,而且非常简单。
这是我的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)