【问题标题】:javascript apply on constructor, throwing "malformed formal parameter"javascript应用于构造函数,抛出“格式错误的形式参数”
【发布时间】:2009-12-24 18:04:08
【问题描述】:

感谢this question 的精彩回复,我了解如何使用可变参数调用 javascript 函数。

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

我发现了一些有趣的信息on this post

但我的代码抛出错误

尝试 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 中使用可变参数调用构造函数?

【问题讨论】:

    标签: javascript constructor variadic-functions


    【解决方案1】:

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

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

    但该解决方案不会考虑原型链。

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

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

    【讨论】:

    • 非常有趣。我已经玩过这个,它在某些情况下非常好用。并非所有构造函数都支持这种方法。 Date,例如:var date = new Date; Date.call(date, 1984, 3, 26)
    • Function.apply(new Function(),arg) /*是单行方案*/
    • 这仅适用于您控制的对象的构造函数 - 大多数构造函数不会一起玩 - 有一个更好的方法,虽然通过制作一个临时构造函数
    【解决方案2】:

    更好的解决方案是创建一个临时构造函数,应用您想要的类的原型(以确保保留原型链),然后手动应用构造函数。这样可以防止不必要地调用构造函数两次...

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

    我测试了性能,发现这个方法其实在非常简单的情况下会慢一些。但是,它只需要在构造函数中构造一个 Date() 对象就可以提高效率。还有,别忘了有些构造函数在没有传参的情况下可能会抛出异常,所以这样也比较正确。

    我的验证码:

    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');
    

    结果:

    first Expensive: 76ms
    second Expensive: 66ms
    first Cheap: 52ms
    second Cheap: 52ms
    

    【讨论】:

    • 我会量化它只有在“ctor”函数很昂贵的情况下才会更好。原因是,这个实现也涉及调用两个函数,只是 tempCtor 是一个空函数。对 ctor (ctor.apply) 的 apply 调用将作为普通函数执行,只有使用 new 关键字才会有构造新对象的开销。
    • 我更新了我的帖子以包含一些测试。虽然对于非常简单的构造函数来说,这个方法确实比较慢,但是让构造函数“昂贵”到足以保证这个方法的程度是微不足道的。此外,正如我在更新中提到的,这种改进的方法可以处理在未提供参数或类型不正确的情况下引发异常的构造函数。
    • 再次更新了我的帖子。现在在简单的情况下同样快,在昂贵的情况下甚至更快。
    • 恕我直言,这是一种比公认答案更好的解决方案。使用 CMS 技术,您可能会初始化对象两次。
    • 一个挑剔的问题:您返回的实例现在是“tempCtor”的实例,而不是 ctor。在所有/大多数情况下都不是大问题,但用户应该了解这一点以及它可能产生的问题。
    【解决方案3】:

    您可以利用可以使用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();
    

    【讨论】:

    • 这并不能完全处理所有情况。您需要在 F() 中从 klass.apply 返回值:function F() { return klass.apply(this, arguments[0]); }。这可以处理构造函数返回要用作实例的值的情况。
    【解决方案4】:

    要完成@CMS 解决方案并保留原型链,您可以这样做:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-25
      • 1970-01-01
      • 1970-01-01
      • 2021-10-24
      • 1970-01-01
      相关资源
      最近更新 更多