Подтвердить что ты не робот

Javascript применяется к конструктору, бросая "неверный формальный параметр"

Благодаря замечательным ответам на этот вопрос Я понимаю, как вызывать функции javascript с помощью varargs.

теперь я использую применимо к конструктору

Я нашел интересную информацию в этом сообщении.

но мой код бросает ошибки

попытка 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 вызвать конструктор с varargs?

4b9b3361

Ответ 1

Вы можете использовать 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);

Ответ 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

Ответ 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();

Ответ 4

Чтобы завершить решение @CMS и сохранить цепочку прототипов, вы можете сделать это:

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

В качестве побочного примечания он не будет работать с IE 8 -.