Héritage JavaScript: Object.create vs new


123

En JavaScript, quelle est la différence entre ces deux exemples:

Prérequis:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

Exemple d'héritage A utilisant Object.create:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

Exemple d'héritage B utilisant le nouveau mot-clé

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

Les deux exemples semblent faire la même chose. Quand choisiriez-vous l'un plutôt que l'autre?

Une question supplémentaire: considérez le code dans le lien ci-dessous (ligne 15), où une référence au propre constructeur de la fonction est stockée dans le prototype. Pourquoi est-ce utile?

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

Extrait (si vous ne souhaitez pas ouvrir le lien):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}

23
Pourquoi diable est-ce marqué comme duplicata!?! L'autre question et les réponses ne sont même pas mentionnées Object.create. Ceci est une erreur et devrait être rouvert.
Scott Rippey

2
Si quoi que ce soit, c'est un double de stackoverflow.com/questions/4166616/…
Scott Rippey

1
13 votes sur ce commentaire et toujours pas rouvert ..?!
TJ

Réponses:


111

Dans votre question, vous avez mentionné que Both examples seem to do the same thingce n'est pas vrai du tout, car

Votre premier exemple

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

Dans cet exemple, vous héritez juste SomeBaseClass' prototypemais que faire si vous avez une propriété dans votre SomeBaseClassgenre

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

et si vous l'utilisez comme

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

L' objobjet n'aura pas de publicPropertypropriété comme dans cet exemple .

Votre deuxième exemple

MyClass.prototype = new SomeBaseClass();

Il exécute la constructorfonction, crée une instance SomeBaseClasset hérite de tout l' SomeBaseClassobjet. Donc, si vous utilisez

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

Dans ce cas, sa publicPropertypropriété est également disponible pour l' objobjet comme dans cet exemple .

Comme le Object.createn'est pas disponible dans certains anciens navigateurs, dans ce cas, vous pouvez utiliser

if(!Object.create)
{
    Object.create=function(o){
        function F(){}
        F.prototype=o;
        return new F();
    }
}

Le code ci-dessus ajoute simplement une Object.createfonction s'il n'est pas disponible afin que vous puissiez utiliser la Object.createfonction et je pense que le code ci-dessus décrit ce que Object.createfait réellement. J'espère que cela aidera d'une certaine manière.


6
Salut Sheikh. Merci pour vos efforts à ce sujet. Oui, la différence est que le constructeur est exécuté dans le deuxième exemple mais pas dans le premier. (ce qui est souhaitable dans mon cas). Concernant la propriété publique non définie qui n'est pas héritée de la super implémentation, il vous suffit d'appeler super dans le constructeur de l'enfant: SomeBaseClass.call (this). Vérifiez ce violon: jsfiddle.net/NhQGB
ChrisRich

J'ai cherché un moyen super simple d'héritage correct dans JS sans utiliser de bibliothèques / frameworks. Je pense que cet exemple (dans mon violon ci-dessus) est la meilleure approche pour les navigateurs modernes. Peut-être que le polyfill Object.Create pourrait ajouter la prise en charge des navigateurs hérités?
ChrisRich

Donc, pour résumer, si vous faites .prototype = new, vous héritez de toutes les valeurs que vous lui avez assignées dans la classe de base, et lorsque vous faites object.create, vous héritez UNIQUEMENT de ce qui se trouve sur le prototype dans la classe de base, non?
davidjnelson

Est-ce que "MyClass.prototype = new SomeBaseClass ()" signifie qu'une nouvelle instance de SomeBaseClass est créée lorsque l'instance de MyClass n'est pas encore créée?

Non, ce n'est que lorsque vous créez l'objet principal que le prototype est défini SomeBaseClass.
The Alpha

39

Les deux exemples semblent faire la même chose.

C'est vrai dans votre cas.

Quand choisiriez-vous l'un plutôt que l'autre?

Quand SomeBaseClassa un corps de fonction, cela serait exécuté avec le newmot - clé. Ce n'est généralement pas prévu - vous souhaitez uniquement configurer la chaîne de prototypes. Dans certains cas, cela peut même causer de graves problèmes car vous instanciez en fait un objet, dont les variables de portée privée sont partagées par toutes les MyClassinstances car elles héritent des mêmes méthodes privilégiées. D'autres effets secondaires sont imaginables.

Donc, vous devriez généralement préférer Object.create. Pourtant, il n'est pas pris en charge dans certains navigateurs hérités; c'est la raison pour laquelle vous voyez l' newapproche beaucoup trop fréquente car elle ne fait souvent aucun mal (évident). Jetez également un œil à cette réponse .


C'est la meilleure réponse, bien expliquée
spex

8

La différence devient évidente si vous utilisez Object.create()comme prévu. En fait, il cache entièrement le prototypemot de votre code, il fera le travail sous le capot. En utilisant Object.create(), nous pouvons aller comme

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

Et puis nous pouvons étendre / hériter d'autres objets de ce

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

C'est donc un autre concept, une manière d'hériter plus "orientée objet". Il n'y a pas de "fonction constructeur" prête à l'emploi en utilisant Object.create()par exemple. Mais bien sûr, vous pouvez simplement créer et appeler une fonction de constructeur auto-définie dans ces objets.

Un argument pour utiliser Object.create()est qu'il peut sembler plus naturel de mélanger / * hériter * d'autres objets que d'utiliser la méthode par défaut de Javascripts .


7
Object.createne peut pas vraiment remplacer l'approche classique avec newquand une fonction de constructeur est nécessaire
Bergi

1
Cela peut très certainement @Bergi. Regardez ce billet de blog: davidwalsh.name/javascript-objects-deconstruction . Vous pouvez également passer un objet d'initialisation comme deuxième paramètre à Object.create ().
LocalPCGuy

@LocalPCGuy: Non, il ne peut pas, car Object.createn'appelle pas une fonction qui serait nécessaire pour créer des fermetures. Bien sûr, avec Object.createvous pouvez mettre initméthode sur vos objets et appel qui immédiatement et peut - être même retourne thispour que vous obtenez une syntaxe concise - mais attendez, c'est un constructeur alors.
Bergi

1
L'appel d'une fonction init fonctionne de la même manière qu'un constructeur, mais ce n'est pas un constructeur. Et cette méthode vous permet de remplacer exactement l'approche classique par Object.create. Et le modèle mental du lien d'objet est beaucoup plus simple que celui créé via «nouveau».
LocalPCGuy

Eh bien, mon modèle mental newutilise le "lien d'objet", donc il n'y a pas beaucoup de différence. En fait, il n'y a que deux choses: .constructorest appelée .init, et cette fonction n'a pas de .prototypepropriété pointant vers votre objet prototype. Le reste n'est que de la syntaxe - et je préfère new Xplus Object.create(x).init().
Bergi

0

Je ne suis pas un expert en script java mais voici un exemple simple pour comprendre la différence entre "Object.create" et "new" ..

étape 1: créez la fonction parent avec quelques propriétés et actions.

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

étape 2: créez une fonction enfant (PersonSalary) qui s'étend au-dessus de la fonction Person à l'aide du nouveau mot clé.

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

étape 3: créez une deuxième fonction enfant (PersonLeaves) qui s'étend au-dessus de la fonction Person en utilisant le mot-clé Object.create .

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

// Vérifiez maintenant les deux prototypes de fonctions enfants.

PersonSalary.prototype
PersonLeaves.prototype

ces deux fonctions enfants seront liées au prototype Person (fonction parent) et pourront accéder à ses méthodes, mais si vous créez une fonction enfant en utilisant new, elle renverra un tout nouvel objet avec toutes les propriétés parent dont nous n'avons pas besoin et aussi lorsque vous en créez objet ou fonction utilisant "New" cette fonction parent est exécutée ce que nous ne voulons pas être.

Voici les plats à emporter

si vous voulez simplement déléguer à certaines méthodes dans la fonction parent et ne voulez pas qu'un nouvel objet soit créé, utiliser Object.create est le meilleur moyen.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.