динамическое построение объектов в javascript?

когда я хочу вызвать функцию в javascript с аргументами, предоставленными из другого места, я могу использовать apply метод функции как:

array = ["arg1", 5, "arg3"] 
...
someFunc.apply(null, array);

но что если мне нужно вызвать конструктор по аналогии? Это, кажется, не работает:

array = ["arg1", 5, "arg3"] 
...
someConstructor.apply({}, array);

по крайней мере, не так, как я пытаюсь:

template = ['string1', string2, 'etc'];
var resultTpl = Ext.XTemplate.apply({}, template);

это не работает вянут:

Ext.XTemplate.prototype.constructor.apply({}, template);

любой способ заставить это работать? (В данном конкретном случае я обнаружил, что new Ext.XTemplate(template) будет работать, но меня интересует общий случай)

подобный вопрос, но специфичный для встроенных типов и без ответа, который я могу использовать: создание экземпляра объекта JavaScript путем вызова prototype.конструктор.применить

спасибо.

Edit:

время прошло, и ES6 и транспиляторы теперь вещь. В ES6 тривиально делать то, что я хотел:new someConstructor(...array). Бабель превратит это в ES5 new (Function.prototype.bind.apply(someConstructor, [null].concat(array)))();, который пояснил в как построить объект JavaScript (используя 'apply')?.

3 ответов


нет простой способ сделать это с помощью функции конструктора. Это потому, что особые вещи происходят, когда вы используете new ключевое слово для вызова функции-конструктора, и поэтому, если вы не собираетесь сделать это, вы должны повторить все эти специальные вещи. Они:

  1. создание нового экземпляра объекта (вы делаете это).
  2. установка внутреннего прототипа этого объекта в функцию конструктора prototype свойство.
  3. настройки объекта constructor собственность.
  4. вызов функции-конструктора с этим экземпляром объекта как this value (вы это делаете).
  5. обработка специального возвращаемого значения из функции конструктора.

я думаю вот и все, но стоит дважды проверить в спец.

поэтому, если вы можете избежать этого и просто использовать функцию конструктора напрямую, я бы сделал что. :-) Если вы не можете, хотя, вы все еще можете это сделать, это просто неудобно и включает обходные пути. (См. также это связано ответа здесь, на StackOverflow, хотя я покрываю всю землю здесь [и затем некоторые].)

ваша самая большая проблема #2 выше: установка внутреннего прототип объекта. Долгое время не было стандартного способа сделать это. Некоторые браузеры поддерживают __proto__ свойство, которое это сделало, поэтому вы можете использовать это, если оно есть. Хорошей новостью является этот ECMAScript 5 вводит способ сделать это явно:Object.create. Таким образом, передовые браузеры, такие как Chrome, будут иметь это. Но если вы имеете дело с браузером, который не имеет ни Object.create, ни __proto__, это становится немного некрасиво:

1) Определите пользовательскую функцию конструктора.

2) установить его prototype свойства prototype свойство функции реального конструктора

3) Используйте его для создания пустого экземпляра объекта.

что ручки прототип вы. Затем вы продолжаете:

4) заменить constructor свойство в этом экземпляре с функцией реального конструктора.

5) вызовите функцию реального конструктора через apply.

6) если возвращаемое значение функции реального конструктора является объектом, используйте его вместо созданного; в противном случае используйте созданный.

что-то вроде этого (видео):

function applyConstruct(ctor, params) {
    var obj, newobj;

    // Use a fake constructor function with the target constructor's
    // `prototype` property to create the object with the right prototype
    function fakeCtor() {
    }
    fakeCtor.prototype = ctor.prototype;
    obj = new fakeCtor();

    // Set the object's `constructor`
    obj.constructor = ctor;

    // Call the constructor function
    newobj = ctor.apply(obj, params);

    // Use the returned object if there is one.
    // Note that we handle the funky edge case of the `Function` constructor,
    // thanks to Mike's comment below. Double-checked the spec, that should be
    // the lot.
    if (newobj !== null
        && (typeof newobj === "object" || typeof newobj === "function")
       ) {
        obj = newobj;
    }

    // Done
    return obj;
}

вы могли бы сделать шаг далее и только использовать поддельный конструктор, если это необходимо, глядя, если Object.create или __proto__ поддерживаются в первую очередь, как это (видео):

function applyConstruct(ctor, params) {
    var obj, newobj;

    // Create the object with the desired prototype
    if (typeof Object.create === "function") {
        // ECMAScript 5 
        obj = Object.create(ctor.prototype);
    }
    else if ({}.__proto__) {
        // Non-standard __proto__, supported by some browsers
        obj = {};
        obj.__proto__ = ctor.prototype;
        if (obj.__proto__ !== ctor.prototype) {
            // Setting it didn't work
            obj = makeObjectWithFakeCtor();
        }
    }
    else {
        // Fallback
        obj = makeObjectWithFakeCtor();
    }

    // Set the object's constructor
    obj.constructor = ctor;

    // Apply the constructor function
    newobj = ctor.apply(obj, params);

    // If a constructor function returns an object, that
    // becomes the return value of `new`, so we handle
    // that here.
    if (typeof newobj === "object") {
        obj = newobj;
    }

    // Done!
    return obj;

    // Subroutine for building objects with specific prototypes
    function makeObjectWithFakeCtor() {
        function fakeCtor() {
        }
        fakeCtor.prototype = ctor.prototype;
        return new fakeCtor();
    }
}

на Chrome 6, выше использует Object.create; в Firefox 3.6 и Opera он использует __proto__. В IE8 он использует функцию поддельного конструктора.

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


С разработчика.mozilla: Связанные функции автоматически подходят для использования с оператором new для создания новых экземпляров, созданных целевой функцией. Когда связанная функция используется для построения значения, предоставленное это игнорируется. Однако предоставленные аргументы по-прежнему добавляются к вызову конструктора.

тем не менее, нам все равно нужно использовать apply для получения аргументов из массива и в вызов bind. Кроме того, нам нужно также сбросить bind функция как этот аргумент функции apply. Это дает нам очень емким один-лайнер, что делает именно так, как нужно.

function constructorApply(ctor, args){
    return new (ctor.bind.apply(ctor, [null].concat(args)))();
};

Я думаю, что другим способом достижения этого может быть расширение класса шаблона Ext, чтобы конструктор нового объекта принимал Ваш массив и делал то, что вы хотите сделать. Таким образом, все конструктивные вещи будут сделаны для вас, и вы можете просто вызвать конструктор своего суперкласса с вашими аргументами.