Pre*_*gar 451 javascript oop inheritance constructor class
在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
Run Code Online (Sandbox Code Playgroud)
答案
从这里的回复中可以清楚地看出,没有内置的方式.apply()
与new
运营商通话.然而,人们提出了一些非常有趣的解决方案.
我首选的解决方案是Matthew Crumley的这个解决方案(我已将其修改为通过该arguments
属性):
var createSomething = (function() {
function F(args) {
return Something.apply(this, args);
}
F.prototype = Something.prototype;
return function() {
return new F(arguments);
}
})();
Run Code Online (Sandbox Code Playgroud)
use*_*621 362
使用ECMAScript5的Function.prototype.bind
东西变得非常干净:
function newCall(Cls) {
return new (Function.prototype.bind.apply(Cls, arguments));
// or even
// return new (Cls.bind.apply(Cls, arguments));
// if you know that Cls.bind has not been overwritten
}
Run Code Online (Sandbox Code Playgroud)
它可以使用如下:
var s = newCall(Something, a, b, c);
Run Code Online (Sandbox Code Playgroud)
甚至直接:
var s = new (Function.prototype.bind.call(Something, null, a, b, c));
var s = new (Function.prototype.bind.apply(Something, [null, a, b, c]));
Run Code Online (Sandbox Code Playgroud)
这个和基于eval的解决方案是唯一始终有效的解决方案,即使使用特殊构造函数,例如Date
:
var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true
Run Code Online (Sandbox Code Playgroud)
编辑
一点解释:我们需要运行new
一个需要有限数量参数的函数.该bind
方法允许我们这样做:
var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();
Run Code Online (Sandbox Code Playgroud)
该anything
参数无关紧要,因为new
关键字重置了f
上下文.但是,出于语法原因需要它.现在,对于bind
调用:我们需要传递可变数量的参数,所以这样做的诀窍:
var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();
Run Code Online (Sandbox Code Playgroud)
让我们把它包装在一个函数中.Cls
作为arugment 0传递,所以它将成为我们的anything
.
function newCall(Cls /*, arg1, arg2, ... */) {
var f = Cls.bind.apply(Cls, arguments);
return new f();
}
Run Code Online (Sandbox Code Playgroud)
实际上,f
根本不需要临时变量:
function newCall(Cls /*, arg1, arg2, ... */) {
return new (Cls.bind.apply(Cls, arguments))();
}
Run Code Online (Sandbox Code Playgroud)
最后,我们应该确保bind
这才是我们真正需要的.(Cls.bind
可能已被覆盖).所以替换它Function.prototype.bind
,我们得到如上所述的最终结果.
Mat*_*ley 259
这里的一个一般化的解决方案,可以调用任何构造(除了不同行为的时称作函数天然构造,如String
,Number
,Date
等)的参数数组:
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
Run Code Online (Sandbox Code Playgroud)
通过调用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);
}
})();
Run Code Online (Sandbox Code Playgroud)
创建和调用外部匿名函数的原因是为了防止函数F
污染全局命名空间.它有时被称为模块模式.
[UPDATE]
对于那些想在TypeScript中使用它的人,因为如果F
返回任何东西,TS会给出错误:
function construct(constructor, args) {
function F() : void {
constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
Run Code Online (Sandbox Code Playgroud)
the*_*eye 30
如果您的环境支持ECMA Script 2015的扩展运算符(...
),您可以像这样使用它
function Something() {
// init stuff
}
function createSomething() {
return new Something(...arguments);
}
Run Code Online (Sandbox Code Playgroud)
注意:现在已发布ECMA Script 2015的规范并且大多数JavaScript引擎正在积极实现它,这将是执行此操作的首选方法.
您可以在此处查看几个主要环境中Spread操作员的支持.
sub*_*ack 27
假设你有一个Items构造函数,它会抛出你抛出的所有参数:
function Items () {
this.elems = [].slice.call(arguments);
}
Items.prototype.sum = function () {
return this.elems.reduce(function (sum, x) { return sum + x }, 0);
};
Run Code Online (Sandbox Code Playgroud)
您可以使用Object.create()创建一个实例,然后使用该实例创建.apply():
var items = Object.create(Items.prototype);
Items.apply(items, [ 1, 2, 3, 4 ]);
console.log(items.sum());
Run Code Online (Sandbox Code Playgroud)
从1 + 2 + 3 + 4 = 10开始运行时打印10:
$ node t.js
10
Run Code Online (Sandbox Code Playgroud)
gfa*_*ess 17
在ES6中,Reflect.construct()
非常方便:
Reflect.construct(F, args)
Run Code Online (Sandbox Code Playgroud)
@Matthew我认为最好修复构造函数属性.
// Invoke new operator with arbitrary arguments
// Holy Grail pattern
function invoke(constructor, args) {
var f;
function F() {
// constructor returns **this**
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
f = new F();
f.constructor = constructor;
return f;
}
Run Code Online (Sandbox Code Playgroud)
@ Matthew答案的改进版本.这个表单通过将temp类存储在一个闭包中获得了一些性能上的好处,以及一个函数可以用来创建任何类的灵活性
var applyCtor = function(){
var tempCtor = function() {};
return function(ctor, args){
tempCtor.prototype = ctor.prototype;
var instance = new tempCtor();
ctor.prototype.constructor.apply(instance,args);
return instance;
}
}();
Run Code Online (Sandbox Code Playgroud)
这将通过调用使用 applyCtor(class, [arg1, arg2, argn]);
你可以将init的东西移到一个单独Something
的原型方法中:
function Something() {
// Do nothing
}
Something.prototype.init = function() {
// Do init stuff
};
function createSomething() {
var s = new Something();
s.init.apply(s, arguments);
return s;
}
var s = createSomething(a,b,c); // 's' is an instance of Something
Run Code Online (Sandbox Code Playgroud)
这个答案有点晚了,但想到任何看到这个的人都可以使用它.有一种方法可以使用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;
Run Code Online (Sandbox Code Playgroud)
对于第一个if
工作的语句,testNew
你必须return this;
在函数的底部.以您的代码为例:
function Something() {
// init stuff
return this;
}
function createSomething() {
return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );
Run Code Online (Sandbox Code Playgroud)
更新:我已经改变了我的第一个例子来加总任意数量的参数,而不仅仅是两个.
我刚遇到这个问题,我解决了这个问题:
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});
Run Code Online (Sandbox Code Playgroud)
是的,它有点难看,但它解决了问题,而且很简单.
小智 5
这有效!
var cls = Array; //eval('Array'); dynamically
var data = [2];
new cls(...data);
Run Code Online (Sandbox Code Playgroud)