2009-10-16 2 views
20

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

// 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); 
+3

Почему этот wiki сообщества? – James

+9

Почему вы не можете использовать ключевое слово 'new'? –

+0

Даниил, на самом деле вы можете, и я неправильно предположил, что в этом случае я не мог. В пятницу днем ​​усталость, я думаю;). – avernet

ответ

18

Разве это не работает?

function factory(class_) { 
    return new class_(); 
} 

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

+1

Условием было не использовать «новый» – Jimmy

+1

Да, да! Это отлично работает. Я подумал (неправильно!), что я не мог бы использовать «новое» ключевое слово в этом случае, но, конечно, не имеет значения, если новый принимает «класс», который был только что определен, или если этот «класс» передается в параметре функции и «новое» называется позже. Я тупой. – avernet

+1

Не работает для меня. Все, что я получаю на FF22, это «TypeError: class_ не является конструктором» – andig

7

Если вы действительно не хотите использовать ключевое слово 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; 
}; 
3

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

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; 
} 
+0

Дима, почему этот браузер более независим от решения, предложенного Дейвом? – avernet

+0

причина «obj .__ proto__ = something» не работает во всех браузерах , а решение Dave не поддерживает аргументы для конструктора –

2

Поскольку 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: The Good Parts by Douglas Crockford:

//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, чтобы получить реализацию для старых браузеров. (См. John Resig's article on new ES5 features including Object.create).

В ES5 вы можете сделать это следующим образом:

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

Я надеюсь, что помогает

25

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

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 
1

Другой способ:

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