JavaScript: comment créer une nouvelle instance d'une classe sans utiliser le nouveau mot-clé?

Je pense que le code suivant rendra la question claire.

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

Cela ne fonctionne-il pas?

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

Je ne comprends pas pourquoi vous ne pouvez pas utiliser le new .

Un moyen plus simple et plus propre sans «usines»

 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 

Si vous ne voulez vraiment pas utiliser le new mot-clé, et vous ne comptez pas seulement pour soutenir Firefox, vous pouvez définir le prototype vous-même. Il n'y a pas vraiment de problème, car vous pouvez simplement utiliser la réponse de Dave Hinton.

 // 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; }; 

Je suppose que la solution indépendante du navigateur serait mieux

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

Parce que JavaScript n'a pas de cours, permettez-moi de reformuler votre question: comment créer un nouvel objet en fonction d'un objet existant sans utiliser le nouveau mot-clé?

Voici une méthode qui n'utilise pas «nouveau». Ce n'est pas strictement une «nouvelle instance», mais c'est la seule façon de penser que cela n'utilise pas «nouveau» (et n'utilise pas les fonctions 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" 

Vous pourriez avoir de la fantaisie et définir le prototype, mais vous rencontrez des problèmes de navigateur et je tente de le simplifier. Vous devrez également utiliser "hasOwnProperty" pour filtrer les propriétés que vous ajoutez au nouvel objet.

Il existe d'autres façons qui utilisent "nouveau", mais qui le cachent. Voici un emprunt de la fonction Object.create en JavaScript: The Good Parts par 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 a la méthode Object.create qui améliorera cette situation, mais elle n'est prise en charge que dans les navigateurs plus récents (p. Ex., IE9, FF4), mais vous pouvez utiliser un polyfill (quelque chose qui remplit les fissures), comme ES5 Shim , Obtenir une implémentation pour les anciens navigateurs. (Voir l'article de John Resig sur les nouvelles fonctionnalités ES5, y compris Object.create ).

Dans ES5, vous pouvez le faire comme ceci:

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

J'espère que ça aide

Autrement:

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