web-dev-qa-db-fra.com

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);
22
avernet

Ça ne marche pas?

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

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

19
dave4420

Une manière 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
27
Cory Martin

Si vous vraiment ne voulez pas utiliser le mot clé new, et que cela ne vous dérange pas de supporter uniquement Firefox, vous pouvez définir le prototype vous-même. Cela ne sert à rien, 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;
};
7
Matthew Crumley

Parce que JavaScript n'a pas de classes, permettez-moi de reformuler votre question: comment créer un nouvel objet basé sur 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 de" mais c'est la seule façon dont je pourrais penser qui n'utilise pas "nouveau" (et n'utilise aucune fonctionnalité 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 j'essaie de garder les choses simples. Vous pouvez également utiliser "hasOwnProperty" pour filtrer les propriétés que vous ajoutez au nouvel objet.

Il existe d'autres façons d'utiliser "nouveau", mais en quelque sorte de le cacher. En voici une qui emprunte à la fonction Object.create dans 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 fera cela beaucoup mieux mais n'est prise en charge que dans les navigateurs plus récents (par exemple, IE9, FF4), mais vous pouvez utiliser un polyfill (quelque chose qui remplit les fissures) , comme ES5 Shim , pour obtenir une implémentation pour les navigateurs plus anciens. (Voir article de John Resig sur les nouvelles fonctionnalités d'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

3
grahamesd

Je suppose qu'une 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;
}
3
Dima Vidmich

Autrement:

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