Использование .apply () с оператором 'new'. Это возможно? - PullRequest
443 голосов
/ 22 октября 2009

В 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

Ответ

Из ответов здесь стало ясно, что нет встроенного способа вызова .apply() с оператором new. Тем не менее, люди предложили ряд действительно интересных решений этой проблемы.

Мое предпочтительное решение было это от Мэтью Крамли (я изменил его, чтобы передать свойство arguments):

var createSomething = (function() {
    function F(args) {
        return Something.apply(this, args);
    }
    F.prototype = Something.prototype;

    return function() {
        return new F(arguments);
    }
})();

Ответы [ 35 ]

0 голосов
/ 14 октября 2016

Хотя другие подходы работоспособны, они неоправданно сложны. В Clojure вы обычно создаете функцию, которая создает экземпляры типов / записей, и используете эту функцию в качестве механизма для создания экземпляров. Переводя это на JavaScript:

function Person(surname, name){
  this.surname = surname;
  this.name = name;
}

function person(surname, name){ 
  return new Person(surname, name);
}

При таком подходе вы избегаете использования new за исключением случаев, описанных выше. И эта функция, конечно же, не имеет проблем при работе с apply или с рядом других функций функционального программирования.

var doe  = _.partial(person, "Doe");
var john = doe("John");
var jane = doe("Jane");

При использовании этого подхода все ваши конструкторы типов (например, Person) являются ванильными, ничего не делающими конструкторами. Вы просто передаете аргументы и присваиваете их свойствам с тем же именем. Волосатые детали входят в функцию конструктора (например, person).

Нет необходимости создавать эти дополнительные функции конструктора, так как они в любом случае являются хорошей практикой. Они могут быть удобными, поскольку они позволяют вам иметь несколько функций конструктора с разными нюансами.

0 голосов
/ 20 мая 2011

Интересно также посмотреть, как проблема повторного использования временного конструктора F() была решена с помощью arguments.callee, или самой функции создателя / фабрики: http://www.dhtmlkitchen.com/?category=/JavaScript/&date=2008/05/11/&entry=Decorator-Factory-Aspect

0 голосов
/ 17 мая 2016

почему вы делаете вещи такими сложными. После new используйте анонимную функцию, которая возвращает функцию конструктора с примененным массивом с аргументами.

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

var newObject = new myConstructor(1,2,3);   // {a: 1, b: 2, c: 3}

var myArguments = [1,2,3];
var anotherObject = new function(){
    return myConstructor.apply(this,myArguments);
  }; // {a: 1, b: 2, c: 3}
0 голосов
/ 10 марта 2016
function F(a){this.a=a}
Z=F;
f=Function('return new function '+F.name+' ()
{return  Z.apply(this,[1]) } ').call()
console.log(f)

function F(a){this.a=a} 
f= new function(){return F.apply(this,[1])} 
console.log(f) 
0 голосов
/ 25 февраля 2016

Да, мы можем, javascript больше по природе prototype inheritance.

function Actor(name, age){
  this.name = name;
  this.age = age;
}

Actor.prototype.name = "unknown";
Actor.prototype.age = "unknown";

Actor.prototype.getName = function() {
    return this.name;
};

Actor.prototype.getAge = function() {
    return this.age;
};

когда мы создаем объект с "new", тогда наш созданный объект INHERITS getAge (), но если мы использовали apply(...) or call(...) для вызова Actor, тогда мы передаем объект для "this", но объект мы передать WON'T наследовать от Actor.prototype

если только мы не передадим apply или вызовем Actor.prototype, но тогда .... "this" будет указывать на "Actor.prototype", а this.name будет записывать в: Actor.prototype.name. Это влияет на все другие объекты, созданные с помощью Actor..., поскольку мы перезаписываем прототип, а не экземпляр

var rajini = new Actor('Rajinikanth', 31);
console.log(rajini);
console.log(rajini.getName());
console.log(rajini.getAge());

var kamal = new Actor('kamal', 18);
console.log(kamal);
console.log(kamal.getName());
console.log(kamal.getAge());

Давайте попробуем с apply

var vijay = Actor.apply(null, ["pandaram", 33]);
if (vijay === undefined) {
    console.log("Actor(....) didn't return anything 
           since we didn't call it with new");
}

var ajith = {};
Actor.apply(ajith, ['ajith', 25]);
console.log(ajith); //Object {name: "ajith", age: 25}
try {
    ajith.getName();
} catch (E) {
    console.log("Error since we didn't inherit ajith.prototype");
}
console.log(Actor.prototype.age); //Unknown
console.log(Actor.prototype.name); //Unknown

Передав Actor.prototype в Actor.call() в качестве первого аргумента, при запуске функции Actor () она выполняет this.name=name, поскольку «this» будет указывать на Actor.prototype, this.name=name; means Actor.prototype.name=name;

var simbhu = Actor.apply(Actor.prototype, ['simbhu', 28]);
if (simbhu === undefined) {
    console.log("Still undefined since the function didn't return anything.");
}
console.log(Actor.prototype.age); //simbhu
console.log(Actor.prototype.name); //28

var copy = Actor.prototype;
var dhanush = Actor.apply(copy, ["dhanush", 11]);
console.log(dhanush);
console.log("But now we've corrupted Parent.prototype in order to inherit");
console.log(Actor.prototype.age); //11
console.log(Actor.prototype.name); //dhanush

Возвращаясь к первоначальному вопросу, как использовать new operator with apply, вот мой дубль ....

Function.prototype.new = function(){
    var constructor = this;
    function fn() {return constructor.apply(this, args)}
    var args = Array.prototype.slice.call(arguments);
    fn.prototype = this.prototype;
    return new fn
};

var thalaivar = Actor.new.apply(Parent, ["Thalaivar", 30]);
console.log(thalaivar);
0 голосов
/ 20 мая 2011

Разве это не должно работать? Полусонный, не читал внимательно.

var Storage = undefined;

return ((Storage = (new Something(...))) == undefined? (undefined) : (Storage.apply(...)));
0 голосов
/ 08 марта 2017

, начиная с ES6, это возможно с помощью оператора Spread, см. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Apply_for_new

Этот ответ уже был, вроде как дан в комментарии https://stackoverflow.com/a/42027742/7049810,, но, похоже, большинство его пропустило

0 голосов
/ 27 февраля 2018

На самом деле самый простой метод:

function Something (a, b) {
  this.a = a;
  this.b = b;
}
function createSomething(){
    return Something;
}
s = new (createSomething())(1, 2); 
// s == Something {a: 1, b: 2}
0 голосов
/ 10 октября 2014

Этот может быть неэффективным способом подойти к этому вопросу, но я думаю, что мне достаточно просто понять.

function createSomething(){
    // use 'new' operator to instantiate a 'Something' object
    var tmp = new Something(); 

    // If the interpreter supports [JavaScript 1.8.5][2], use 'Object.create'
    // var tmp = Object.create(Something.prototype); 

    // calling the constructor again to initialize the object
    Something.apply(tmp, arguments); 
    return tmp;
}
0 голосов
/ 31 июля 2011

Благодаря сообщениям здесь я использовал это следующим образом:

SomeClass = function(arg1, arg2) {
    // ...
}

ReflectUtil.newInstance('SomeClass', 5, 7);

и реализация:

/**
 * @param strClass:
 *          class name
 * @param optionals:
 *          constructor arguments
 */
ReflectUtil.newInstance = function(strClass) {
    var args = Array.prototype.slice.call(arguments, 1);
    var clsClass = eval(strClass);
    function F() {
        return clsClass.apply(this, args);
    }
    F.prototype = clsClass.prototype;
    return new F();
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...