在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);
}
})();
不含ES6或polyfills的解决方案:
var obj = _new(Demo).apply(["X", "Y", "Z"]);
function _new(constr)
{
function createNamedFunction(name)
{
return (new Function("return function " + name + "() { };"))();
}
var func = createNamedFunction(constr.name);
func.prototype = constr.prototype;
var self = new func();
return { apply: function(args) {
constr.apply(self, args);
return self;
} };
}
function Demo()
{
for(var index in arguments)
{
this['arg' + (parseInt(index) + 1)] = arguments[index];
}
}
Demo.prototype.tagged = true;
console.log(obj);
console.log(obj.tagged);
输出
演示{arg1: "X", arg2: "Y", arg3: "Z"}
…或者“更短”的方式:
var func = new Function("return function " + Demo.name + "() { };")();
func.prototype = Demo.prototype;
var obj = new func();
Demo.apply(obj, ["X", "Y", "Z"]);
编辑:
我认为这可能是一个很好的解决方案:
this.forConstructor = function(constr)
{
return { apply: function(args)
{
let name = constr.name.replace('-', '_');
let func = (new Function('args', name + '_', " return function " + name + "() { " + name + "_.apply(this, args); }"))(args, constr);
func.constructor = constr;
func.prototype = constr.prototype;
return new func(args);
}};
}
这个答案有点晚了,但我想任何看到这个的人都可以使用它。有一种方法可以使用apply返回一个新对象。尽管它需要对对象声明做一点更改。
function testNew() {
if (!( this instanceof arguments.callee ))
return arguments.callee.apply( new arguments.callee(), arguments );
this.arg = Array.prototype.slice.call( arguments );
return this;
}
testNew.prototype.addThem = function() {
var newVal = 0,
i = 0;
for ( ; i < this.arg.length; i++ ) {
newVal += this.arg[i];
}
return newVal;
}
testNew( 4, 8 ) === { arg : [ 4, 8 ] };
testNew( 1, 2, 3, 4, 5 ).addThem() === 15;
为了让第一个if语句在testNew中工作,你必须返回这个;在函数的底部。以代码为例:
function Something() {
// init stuff
return this;
}
function createSomething() {
return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );
更新:我改变了我的第一个例子的总和参数的数量,而不是只有两个。
Matthew Crumley在CoffeeScript中的解决方案:
construct = (constructor, args) ->
F = -> constructor.apply this, args
F.prototype = constructor.prototype
new F
or
createSomething = (->
F = (args) -> Something.apply this, args
F.prototype = Something.prototype
return -> new Something arguments
)()
修改了@Matthew的答案。在这里,我可以像往常一样传递任意数量的参数给函数(不是数组)。此外,'Something'也没有硬编码:
function createObject( constr ) {
var args = arguments;
var wrapper = function() {
return constr.apply( this, Array.prototype.slice.call(args, 1) );
}
wrapper.prototype = constr.prototype;
return new wrapper();
}
function Something() {
// init stuff
};
var obj1 = createObject( Something, 1, 2, 3 );
var same = new Something( 1, 2, 3 );
这是我的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)