JavaScript: как создать новый экземпляр класса без использования ключевого слова new?

Я думаю, что следующий код прояснит вопрос.

// My class
var Class = function() { console.log("Constructor"); };
Class.prototype = { method: function() { console.log("Method");} }

// Creating an instance with new
var object1 = new Class();
object1.method();
console.log("New returned", object1);

// How to write a factory which can't use the new keyword?
function factory(clazz) {
    // Assume this function can't see "Class", but only sees its parameter "clazz".
    return clazz.call(); // Calls the constructor, but no new object is created
    return clazz.new();  // Doesn't work because there is new() method
};

var object2 = factory(Class);
object2.method();
console.log("Factory returned", object2);

person Community    schedule 16.10.2009    source источник
comment
Почему вы не можете использовать ключевое слово new?   -  person Daniel Pryden    schedule 17.10.2009
comment
Дэниел, на самом деле ты можешь, и я ошибочно предположил, что не могу в данном случае. Думаю, усталость после обеда в пятницу;).   -  person avernet    schedule 17.10.2009
comment
JP, чтобы другие могли прояснить вопрос, если необходимо.   -  person avernet    schedule 17.10.2009
comment
Самый странный «все еще открытый» вопрос сообщества.   -  person Léon Pelletier    schedule 08.01.2013
comment
я создал декоратор, чтобы справиться с этим, пожалуйста, проверьте мой ответ здесь: stackoverflow.com/a/44061744/427622   -  person Fareed Alnamrouti    schedule 19.05.2017


Ответы (6)


Это не работает?

function factory(class_, ...arg) {
    return new class_(...arg);
}

Я не понимаю, почему вы не можете использовать new.

person Community    schedule 16.10.2009
comment
условие не использовало новых - person Jimmy; 17.10.2009
comment
Да черт возьми! Это прекрасно работает. Я думал (ошибочно!), Что не могу использовать ключевое слово new в этом случае, но, конечно, не имеет значения, принимает ли new класс, который был только что определен, или этот класс передается в параметре функции и новый называется позже. Я глупый. - person avernet; 17.10.2009
comment
У меня не работает. Все, что я получаю от FF22, это TypeError: class_ не конструктор - person andig; 27.06.2013
comment
хорошо, горячо, как применить список аргументов к этому недавно созданному классу? ;) новый класс _ (). apply (this, array) Я так не думаю - person Lpc_dark; 24.05.2014
comment
@Lpc_dark попадает в самую точку! Может ты мог бы сделать function factory(class_, ...args) {return new class_(...args)}? - person Stijn de Witt; 07.04.2017

Более простой и чистый способ без "фабрик"

function Person(name) {
  if (!(this instanceof Person)) return new Person(name);
  this.name = name;
}

var p1 = new Person('Fred');
var p2 = Person('Barney');

p1 instanceof Person  //=> true
p2 instanceof Person  //=> true
person Community    schedule 31.05.2012

Если вы действительно не хотите использовать ключевое слово new и не возражаете против поддержки только Firefox, вы можете установить прототип самостоятельно. Хотя в этом нет никакого смысла, так как вы можете просто использовать ответ Дэйва Хинтона.

// This is essentially what the new keyword does
function factory(clazz) {
    var obj = {};
    obj.__proto__ = clazz.prototype;
    var result = clazz.call(obj);
    return (typeof result !== 'undefined') ? result : obj;
};
person Community    schedule 16.10.2009

Я думаю, что решение, независимое от браузера, было бы лучше

function empty() {}

function factory(clazz /*, some more arguments for constructor */) {
    empty.prototype = clazz.prototype;
    var obj = new empty();
    clazz.apply(obj, Array.prototype.slice.call(arguments, 1));
    return obj;
}
person Community    schedule 11.04.2010
comment
Дима, почему этот браузер более независимый, чем решение, предложенное Дейвом? - person avernet; 12.04.2010
comment
причина obj .__ proto__ = что-то не работает во всех браузерах, а решение Дейва не поддерживает аргументы для конструктора - person Dima Vidmich; 12.04.2010

Поскольку в JavaScript нет классов, позвольте мне перефразировать ваш вопрос: как создать новый объект на основе существующего без использования ключевого слова new?

Вот метод, который не использует «новый». Это не совсем «новый экземпляр», но это единственный способ, о котором я мог думать, без использования «нового» (и без использования каких-либо функций ECMAScript 5).

//a very basic version that doesn't use 'new'
function factory(clazz) {
    var o = {};
    for (var prop in clazz) {
        o[prop] = clazz[prop];
    }
    return o;
};

//test
var clazz = { prop1: "hello clazz" };
var testObj1 = factory(clazz);
console.log(testObj1.prop1);    //"hello clazz" 

Вы можете пофантазировать и установить прототип, но тогда вы столкнетесь с проблемами кроссбраузерности, и я стараюсь не усложнять. Также вы можете использовать hasOwnProperty для фильтрации того, какие свойства вы добавляете к новому объекту.

Есть и другие способы использовать слово «новое», но как бы его скрыть. Вот тот, который заимствован из функции Object.create в JavaScript: Хорошие моменты Дугласа Крокфорда:

//Another version the does use 'new' but in a limited sense
function factory(clazz) {
    var F = function() {};
    F.prototype = clazz;
    return new F();
};

//Test
var orig = { prop1: "hello orig" };
var testObj2 = factory(orig);
console.log(testObj2.prop1);  //"hello orig"

В EcmaScript 5 есть метод Object.create, который делает это намного лучше, но поддерживается только в новых браузерах (например, IE9, FF4), но вы можете использовать polyfill (то, что заполняет трещины), например ES5 Shim, чтобы получить реализацию для старых браузеров. (См. статью Джона Ресига о новых функциях ES5, включая Object.create ).

В ES5 это можно сделать так:

//using Object.create - doesn't use "new"
var baseObj = { prop1: "hello base" };
var testObj3 = Object.create(baseObj);
console.log(testObj3.prop1);

Надеюсь, это поможет

person Community    schedule 22.04.2011

По-другому:

var factory = function(clazz /*, arguments*/) {
    var args = [].slice.call(arguments, 1);
    return new function() { 
        clazz.apply(this, args)
    }
}
person Community    schedule 04.06.2013