在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);
}
})();
你不能像new操作符那样调用带有可变数量参数的构造函数。
你能做的就是稍微改变构造函数。而不是:
function Something() {
// deal with the "arguments" array
}
var obj = new Something.apply(null, [0, 0]); // doesn't work!
你可以这样做:
function Something(args) {
// shorter, but will substitute a default if args.x is 0, false, "" etc.
this.x = args.x || SOME_DEFAULT_VALUE;
// longer, but will only put in a default if args.x is not supplied
this.x = (args.x !== undefined) ? args.x : SOME_DEFAULT_VALUE;
}
var obj = new Something({x: 0, y: 0});
或者如果你必须使用数组:
function Something(args) {
var x = args[0];
var y = args[1];
}
var obj = new Something([0, 0]);
这是一个通用的解决方案,可以调用任何构造函数(除了本机构造函数,当作为函数调用时行为不同,如String, Number, Date等)与参数数组:
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
通过调用construct(Class,[1,2,3])创建的对象将与用new Class(1,2,3)创建的对象相同。
您还可以创建一个更具体的版本,这样就不必每次都传递构造函数。这也稍微更有效,因为它不需要每次调用时都创建内部函数的新实例。
var createSomething = (function() {
function F(args) {
return Something.apply(this, args);
}
F.prototype = Something.prototype;
return function(args) {
return new F(args);
}
})();
这样创建和调用外部匿名函数的原因是为了防止函数F污染全局命名空间。它有时被称为模块模式。
(更新)
对于那些想在TypeScript中使用它的人,因为如果F返回任何东西,TS会给出一个错误:
function construct(constructor, args) {
function F() : void {
constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
感谢这里的帖子,我这样使用它:
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();
};