24

感谢对这个问题的精彩回答,我了解如何使用可变参数调用 javascript 函数。

现在我希望将apply 与构造函数一起使用

我在这篇文章中发现了一些有趣的信息。

但我的代码抛出错误

尝试1:

var mid_parser = new Parser.apply(null, mid_patterns);

错误:

TypeError: Function.prototype.apply called on incompatible [object Object]

尝试 2: 尝试 1:

var mid_parser = new Parser.prototype.apply(null, mid_patterns);

错误:

TypeError: Function.prototype.apply called on incompatible [object Object]

尝试2:

function Parser()
{
    this.comparemanager = new CompareManager(arguments);
}

mid_patterns = [objA,objB,objC]
var mid_parser = new Parser();
Parser.constructor.apply(mid_parser, mid_patterns);

错误:

syntax_model.js:91: SyntaxError: malformed formal parameter

尝试3:

var mid_parser = Parser.apply(null, mid_patterns);

错误 :

TypeError: this.init is undefined // init is a function of Parser.prototype

我暂时有一个解决方法:

function Parser()
{
    if(arguments.length) this.init.call(this,arguments); // call init only if arguments
}
Parser.prototype = {
   //...
   init: function()
   {
         this.comparemanager = new CompareManager(arguments);
   }
   //...
}

var normal parser = new Parser(objA,objB,objC);

mid_patterns = [objA,objB,objC]
var dyn_parser = new Parser();
dyn_parser.init.apply(dyn_parser, mid_patterns);

这工作得很好,但它不像我想要的那样干净和通用。

javascript中是否可以使用可变参数调用构造函数?

4

4 回答 4

17

您可以使用 apply 并传递一个空对象作为this参数:

var mid_parser = {};
Parser.apply(mid_parser, mid_patterns);

但是该解决方案不会关心原型链。

您可以使用运算符创建一个Parser对象,new但不传递参数,然后使用apply它重新运行构造函数:

var mid_parser = new Parser();
Parser.apply(mid_parser, mid_patterns);
于 2009-12-24T18:13:05.633 回答
11

A better solution is to create a temporary constructor function, apply the prototype of the class that you want (to ensure prototype chains are preserved) and then apply the constructor manually. This prevents calling the constructor twice unnecessarily...

applySecond = function(){
    function tempCtor() {};
    return function(ctor, args){
        tempCtor.prototype = ctor.prototype;
        var instance = new tempCtor();
        ctor.apply(instance,args);
        return instance;
    }
}();

I tested the performance and found that this method is, in fact, a bit slower in the very simple case. However, it only takes the construction of a single Date() object in the constructor for this to be more efficient. Also, don't forget that some constructors may throw exceptions if there are no parameters passed, so this is also more correct.

My validation code:

var ExpensiveClass = function(arg0,arg1){
    this.arg0 = arg0;
    this.arg1 = arg1;
    this.dat = new Date();
}

var CheapClass = function(arg0,arg1){
    this.arg0 = arg0;
    this.arg1 = arg1;
}

applyFirst = function(ctor, args){
    var instance = new ctor();
    ctor.apply(instance, args);
    return instance;
}

applySecond = function(){
    function tempCtor() {};
    return function(ctor, args){
        tempCtor.prototype = ctor.prototype;
        var instance = new tempCtor();
        ctor.apply(instance,args);
        return instance;
    }
}();

console.time('first Expensive');
for(var i = 0; i < 10000; i++){
    test = applyFirst(ExpensiveClass ,['arg0','arg1']);
}
console.timeEnd('first Expensive');

console.time('second Expensive');
for(var i = 0; i < 10000; i++){
    test = applySecond(ExpensiveClass ,['arg0','arg1']);
}
console.timeEnd('second Expensive');

console.time('first Cheap');
for(var i = 0; i < 10000; i++){
    test = applyFirst(CheapClass,['arg0','arg1']);
}
console.timeEnd('first Cheap');

console.time('second Cheap');
for(var i = 0; i < 10000; i++){
    test = applySecond(CheapClass,['arg0','arg1']);
}
console.timeEnd('second Cheap');

The results:

first Expensive: 76ms
second Expensive: 66ms
first Cheap: 52ms
second Cheap: 52ms
于 2010-10-25T23:22:18.650 回答
7

您可以利用这样一个事实,即您可以使用链式构造函数apply(...)来实现这一点,尽管这需要创建一个代理类。下面的construct()功能可以让你做到:

var f1 = construct(Foo, [2, 3]);
// which is more or less equivalent to
var f2 = new Foo(2, 3);

construct()功能:

function construct(klass, args) {

  function F() {
    return klass.apply(this, arguments[0]); 
  }; 

  F.prototype = klass.prototype; 

  return new F(args);

}

一些使用它的示例代码:

function Foo(a, b) {
  this.a = a; this.b = b;
}

Foo.prototype.dump = function() {
  console.log("a = ", this.a);
  console.log("b = ", this.b);
};

var f = construct(Foo, [7, 9]);

f.dump();
于 2010-01-28T12:34:43.920 回答
0

要完成@CMS 解决方案并保留原型链,您可以执行以下操作:

var mid_parser = {};
mid_parser.__proto__ = Parser.prototype;
Parser.apply(mid_parser, mid_patterns);

附带说明,它不适用于 IE 8-。

于 2012-10-09T21:49:09.013 回答