Comprendre pourquoi le véritable héritage prototypique est meilleur que l'héritage classique / pseudo-prototypique et pourquoi je ne devrais pas utiliser "nouveau"

Lire des articles de Aadit M Shah comme Why Prototypal Inheritance Matters ou arrêter d'utiliser des fonctions de constructeur en JavaScript d'Eric Elliott, je pense que je comprends tous leurs arguments, en théorie. Mais en pratique, je ne vois pas les avantages réels de ce modèle.

Examinons deux implémentations à partir de deux extraits pour faire l'héritage.

  1. Le premier utilise aument.js c'est un script de Aadit M Shah
  2. Sur cet exemple, nous allons utiliser ce script . Est aussi fait par Aadit M Shah.

Mise en œuvre 1 :

var AugmentPerson = Object.augment(function() { this.constructor = function(name) { this.name = name; }; this.setAddress = function(country, city, street) { this.country = country; this.city = city; this.street = street; }; }); var AugmentFrenchGuy = AugmentPerson.augment(function(base) { this.constructor = function(name) { base.constructor.call(this,name); }; this.setAddress = function(city, street) { base.setAddress.call(this, "France", city, street); }; }); var AugmentParisLover = AugmentFrenchGuy.augment(function(base) { this.constructor = function(name) { base.constructor.call(this, name); }; this.setAddress = function(street) { base.setAddress.call(this, "Paris", street); }; }); var t = new AugmentParisLover("Mary"); t.setAddress("CH"); console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH 

Dans cet exemple, nous utilisons des constructeurs de fonctions au lieu d'hériter directement d'un objet.

Mise en œuvre 2 :

  var CreatePerson = { create: function (name) { this.name = name; return this.extend(); }, setAddress: function(country, city, street) { this.country = country; this.city = city; this.street = street; } }; var CreateFrenchGuy = CreatePerson.extend({ create: function (name) { return CreatePerson.create.call(this,name); }, setAddress: function(city, street) { CreatePerson.setAddress('France', city, street); } }); var CreateParisLover = CreateFrenchGuy.extend({ create: function (name) { return CreateFrenchGuy.create.call(this,name); }, setAddress: function(street) { CreateFrenchGuy.setAddress('Paris', street); } }); var t = CreateParisLover.create("Mary"); t.setAddress("CH"); console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH 

Pour être honnête, j'essaie de voir les avantages de la deuxième implémentation. Mais je ne suis pas capable. Le seul point que je vois est plus flexible, c'est parce que nous pouvons créer l'instance en utilisant:

var t = CreateParisLover.create.apply(CreateParisLover, ["Mary"]);

Cela nous donne plus de flexibilité, c'est vrai. Mais nous pouvons faire de même avec ceci :

  Function.prototype.new = function () { function functor() { return constructor.apply(this, args); } var args = Array.prototype.slice.call(arguments); functor.prototype = this.prototype; var constructor = this; return new functor; }; 

Alors nous pouvons:

 var t = AugmentParisLover.new.apply(AugmentParisLover, ["Mary"]); 

Quels sont les avantages réels en termes de flexibilité, de réutilisation, de difficulté … Parce que si vous vérifiez les performances des deux cas. Object.create () est à peu près plus lent que neuf: http://jsperf.com/inheritance-using-create-vs-new. Je suis déroutant.

Des questions similaires ont été posées et répondu plusieurs fois auparavant. Voir:

Fonction constructeur vs fonctions d'usine Classique Vs héritage prototypique

Plus d'apprentissage: https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t http://vimeo.com/69255635

Tl dr

  • Les constructeurs enfreignent le principe ouvert / fermé
  • Les constructeurs fusionnent la création d'objet avec l'initialisation des objets – entravant parfois la réutilisation du code
  • Les constructeurs ressemblent un peu à des classes, ce qui confond. JavaScript n'a pas besoin de cours (je recommande d'éviter le mot-clé de classe dans ES6). JavaScript a quelque chose de mieux que les classes.
  • La combinaison de délégation de prototype et d'extension d'objet dynamique (héritage concaténatif) est beaucoup plus puissante et flexible que l'héritage classique.
  • Les connexions entre Constructor.prototype et les instances sont fragiles et peu fiables en JavaScript. L'utilisation de constructeurs peut fournir l'illusion d'une instance de travail, ce qui pourrait être source de confusion lorsqu'il ne fonctionne pas dans des contextes d'exécution ou ne fonctionne pas si le prototype du constructeur est échangé.
  • L'échange du prototype est plus difficile avec les constructeurs. Vous pouvez le faire pour permettre la construction d'objets polymorphes. Avec les usines, les prototypes remplaçables à chaud sont faciles et peuvent être réalisés en utilisant .call () et .apply ().

Modifier – répondre à la "réponse" publiée par l'OP:

La meilleure chose à propos d'Object.create est qu'il s'agit d'un outil dédié et de bas niveau qui vous permet de créer un nouvel objet et d'attribuer tout prototype que vous le souhaitez sans utiliser une fonction de constructeur. Il y a beaucoup de raisons pour éviter les constructeurs, abordés en profondeur ici: fonction constructeur vs fonctions d'usine

  1. Le code que vous utilisez pour démontrer «moins de code» ne démontre pas vraiment la différence entre l'héritage classique et le patrimoine prototypique. Un exemple plus typique pourrait ressembler à:

Classique

 var Animal = function Animal(name) { this.name = name; }; Animal.prototype.walk = function walk() { console.log(this.name + ' goes for a walk.'); }; var Rabbit = function Rabbit(/* name */) { // Because construction and instantiation are conflated, you must call super(). Animal.prototype.constructor.apply(this, arguments); }; // Classical inheritance is really built on top of prototypal inheritance: Rabbit.prototype = Object.create(Animal.prototype); // Fix the .constructor property: Rabbit.prototype.constructor = Rabbit; Rabbit.prototype.jump = function jump() { console.log(this.name + ' hops around a bit.'); }; var myRabbit = new Rabbit('Bunny George'); myRabbit.walk(); // Bunny George goes for a walk. 

Prototypique

 var animalMethods = { walk: function walk() { console.log(this.name + ' goes for a walk.'); } }; var animal = function animal(name) { var instance = Object.create(animalMethods); instance.name = name; return instance; }; var rabbitMethods = { jump: function jump() { console.log(this.name + ' hops around a bit.'); } }; var rabbit = function rabbit(name) { var proto = rabbitMethods; // This is more commonly done like mixin({}, animalMethods, rabbitMethods); // where mixin = $.extend, _.extend, mout.object.mixIn, etc... It just copies // source properties to the destination object (first arg), where properties from // the last argument override properties from previous source arguments. proto.walk = animalMethods.walk; var instance = Object.create(rabbitMethods); // This could just as easily be a functional mixin, // shared with both animal and rabbit. instance.name = name; return instance; }; var rabbit2 = rabbit('Bunny Bob'); rabbit2.walk(); // Bunny Bob goes for a walk. 

La quantité de code requise est assez similaire, mais pour moi, il est beaucoup plus clair ce que font les choses prototypes, et c'est beaucoup plus flexible et n'a aucun des bagages arthritiques des héritages classiques du premier exemple.

La programmation ressemble beaucoup à la mode. Subconsciemment, la plupart des programmeurs écrivent un code qui leur paraît plaisant de façon esthétique. C'est la raison principale pour laquelle les programmeurs Java veulent implémenter l'héritage classique en JavaScript. Oui, essayer de mettre en place l'héritage classique en JavaScript est une tâche monolithique, mais cela n'empêche pas les gens de le faire. C'est une overkill, mais les gens le font toujours parce qu'ils veulent simplement que leur code ressemble à des classes (p. Ex . JTypes ).

De la même manière, Eric et moi essayons de populariser l'utilisation des fonctions d'usine au lieu des fonctions du constructeur. Cependant, ce passage des usines aux constructeurs n'est pas seulement pour des raisons esthétiques. Nous essayons tous deux de changer la mentalité des programmeurs JavaScript car, dans certains aspects, nous pensons tous deux que le JavaScript est fondamentalement défectueux. Le new opérateur en JavaScript est un tel aspect. Bien qu'il soit cassé mais c'est central pour la langue et donc il ne peut pas être évité.

Le résultat est le suivant:

Si vous souhaitez créer des chaînes prototypes en JavaScript, vous devez l'utiliser à new . Il n'y a pas d'autre moyen de le faire (sauf .__proto__ qui est mal vu).

Fait intéressant, vous n'avez besoin ni de prototypes ni de classes pour hériter d'objets multiples. En utilisant la composition de l'objet, vous pouvez obtenir un sous-typage comportemental fort en JavaScript, car Benjamin Gruenbaum décrit la réponse suivante: https://stackoverflow.com/a/17008693/783743

Dans cette réponse, je vais aborder les sujets suivants:

  1. Pourquoi sommes-nous coincés avec de new ?
  2. Pourquoi les usines sont-elles meilleures que les constructeurs?
  3. Comment obtenons-nous le meilleur des deux mondes?

1. Pourquoi sommes-nous coincés avec de new ?

Le new mot-clé est mis sur un socle en JavaScript. Il n'y a aucun moyen de créer une chaîne de prototypes en JavaScript sans utiliser de new . Oui, vous pouvez modifier la propriété .__proto__ d'un objet, mais seulement après sa création, et cette pratique est mal interprétée. Même Object.create utilise de new interne:

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

Comme Douglas Crockford l'a mentionné :

La fonction Object.create le modèle de constructeur de JavaScript, atteignant un véritable héritage prototypique. Il prend un ancien objet en tant que paramètre et renvoie un nouvel objet vide qui hérite de l'ancien. Si nous essayons d'obtenir un membre du nouvel objet, et qu'il manque de cette clé, l'ancien objet fournira le membre. Les objets héritent des objets. Quel pourrait être plus orienté objet que celui-là?

Le fait est que, bien que le new mot-clé en JavaScript soit «enchevêtré», il n'y a pas d'autre moyen de créer des chaînes prototypes en JavaScript. La fonction Object.create , même si elle est implémentée en mode natif, est encore plus lente que l'utilisation de new et, par conséquent, pour des raisons de performance, la plupart des personnes utilisent encore de new même si Object.create est une option plus logiquement sonore.

2. Pourquoi les usines sont-elles meilleures que les constructeurs?

Maintenant, vous pourriez vous demander si new est vraiment si grave. Après toute performance, c'est en effet la meilleure solution. À mon avis, cela ne devrait pas l'être. Que vous Object.create performances new ou Object.create devraient toujours être identiques. C'est là que les implémentations de langue manquent. Ils devraient vraiment s'efforcer de rendre Object.create plus rapidement. Donc, en plus de la performance, de new ont-elles d'autres qualités de rachat? À mon humble avis, ce n'est pas le cas.

Souvent, vous ne savez pas vraiment ce qui ne va pas avec une langue avant de commencer à utiliser une meilleure langue. Alors, voyons d'autres langues:

A) Utile

Magpie est une langue de loisir créée par Bob Nystrom . Il a une foule de fonctionnalités très intéressantes qui interagissent très bien les uns avec les autres, à savoir:

  1. Motifs
  2. Des classes
  3. Multimethods

Les classes de Magpie sont cependant plus proches des prototypes de JavaScript ou des types de données dans Haskell.

Dans Magpie, l'instanciation des classes se divise en deux étapes:

  1. Construire une nouvelle instance.
  2. Initialisation de l'instance nouvellement construite.

En JavaScript, le new mot-clé combine la construction et l'initialisation des instances. C'est en fait une mauvaise chose car, comme nous le verrons bientôt, le fractionnement de la construction et de l'initialisation est en fait une bonne chose.

Considérez le code Magpie suivant:

 defclass Point var x var y end val zeroPoint = Point new(x: 0, y: 0) def (this == Point) new (x is Int, y is Int) match x, y case 0, 0 then zeroPoint else this new(x: x, y: y) end end var origin = Point new(0, 0) val point = Point new(2, 3) 

Ceci équivaut au code JavaScript suivant:

 function Point(x, y) { this.x = x; this.y = y; } var zeroPoint = new Point(0, 0); Point.new = function (x, y) { return x === 0 && y === 0 ? zeroPoint : new Point(x, y); }; var origin = Point.new(0, 0); var point = Point.new(2, 3); 

Comme vous pouvez le voir ici, nous avons divisé la construction et l'initialisation des instances en deux fonctions. La fonction Point initialise l'instance et la fonction Point.new construit l'instance. Essentiellement, nous avons simplement créé une fonction d'usine.

La séparation de la construction à partir de l'initialisation est un modèle si utile que les bonnes personnes de la salle JavaScript ont même bloggé à ce sujet, l'appelant le modèle d'initialisation . Vous devriez lire sur le modèle d'initialisation. Cela vous montre que l'initialisation dans JavaScript est distincte de la construction.

  1. Des usines comme Object.create (+1): la construction est distincte de l'initialisation.
  2. Le new opérateur (-1): la construction et l'initialisation sont inséparables.

B) Haskell

JavaScript est mon langage préféré depuis 8 ans. Récemment cependant, j'ai commencé à programmer à Haskell et je dois avouer que Haskell m'a volé mon cœur. La programmation à Haskell est amusante et passionnante. JavaScript a encore un long chemin à parcourir avant qu'il ne soit dans la même ligue que Haskell et il y a beaucoup que les programmeurs JavaScript peuvent apprendre de Haskell. J'aimerais parler de types de données algébriques de Haskell à propos de cette question.

Les types de données dans Haskell sont comme des prototypes dans JavaScript et les constructeurs de données à Haskell sont comme des fonctions d'usine en JavaScript. Par exemple, la classe Point ci-dessus serait écrite comme suit dans Haskell:

 data Point = Point Int Int zeroPoint = Point 0 0 origin = zeroPoint point = Point 2 3 

Succès n'est-ce pas? Cependant, je ne suis pas là pour vendre Haskell, alors regardons quelques autres fonctionnalités que Haskell offre:

 data Shape = Rectangle Point Point | Circle Point Int rectangle = Rectangle origin (Point 3 4) circle = Circle zeroPoint 3 

Ici rectangle et circle sont les deux exemples de type Shape :

 rectangle :: Shape circle :: Shape 

Dans ce cas, Shape est notre prototype (type de données dans Haskell) et le rectangle et le circle sont des instances de ce type de données. Plus intéressant cependant, le prototype Shape comporte deux constructeurs (constructeurs de données à Haskell): Rectangle et Circle .

 Rectangle :: Point -> Point -> Shape Circle :: Point -> Int -> Shape 

Le constructeur de données Rectangle est une fonction qui prend un Point et un autre Point et renvoie une Shape . De même, le constructeur de données Circle est une fonction qui prend un Point et un Int et renvoie une Shape . En JavaScript, ceci serait écrit comme suit:

 var Shape = {}; Rectangle.prototype = Shape; function Rectangle(p1, p2) { this.p1 = p1; this.p2 = p2; } Circle.prototype = Shape; function Circle(p, r) { this.p = p; this.r = r; } var rectangle = new Rectangle(origin, Point.new(3, 4)); var circle = new Circle(zeroPoint, 3); 

Comme vous pouvez le voir, un prototype en JavaScript peut avoir plus d'un constructeur et cela a du sens. Il est également possible pour un constructeur d'avoir des prototypes différents à des moments différents mais cela n'a aucun sens du tout. Cela pourrait provoquer une rupture de instanceof .

Il s'avère qu'avec plusieurs constructeurs est une douleur lors de l'utilisation du modèle constructeur. Cependant, c'est un match fait au paradis lors de l'utilisation du prototype:

 var Shape = { Rectangle: function (p1, p2) { var rectangle = Object.create(this); rectangle.p1 = p1; rectangle.p2 = p2; return rectangle; }, Circle: function (p, r) { var circle = Object.create(this); circle.p = p; circle.r = r; return circle; } }; var rectangle = Shape.Rectangle(zeroPoint, Point.new(3, 4)); var circle = Shape.Circle(origin, 3); 

Vous pouvez également utiliser la fonction d' extend partir de la publication de mon blog sur Pourquoi l'hétérogénéité Prototypal importe pour rendre le code ci-dessus plus succinct:

 var Shape = { Rectangle: function (p1, p2) { return this.extend({ p1: p1, p2: p2 }); }, Circle: function (p, r) { return this.extend({ p: p, r: r }); } }; var rectangle = Shape.Rectangle(zeroPoint, Point.new(3, 4)); var circle = Shape.Circle(origin, 3); 

Les usines écrites de cette façon ressemblent beaucoup au modèle de module et il est naturel d'écrire un code comme celui-ci. Contrairement au modèle de constructeur, tout est bien enveloppé dans un objet littéral. Rien n'est pendu ici, là et partout.

Néanmoins, si la performance est votre principale préoccupation, respectez le modèle constructeur et new . À mon avis, cependant, les moteurs JavaScript modernes sont assez rapides pour que les performances ne soient plus le facteur principal. Au lieu de cela, je pense que les programmeurs JavaScript devraient investir plus de temps dans l'écriture de code qui est maintenable et robuste, et le modèle prototype est en effet plus élégant et compréhensible que le modèle constructeur.

  1. Usines (+1): vous pouvez facilement créer plusieurs usines pour chaque prototype.
  2. Constructeurs (-1): la création de plusieurs constructeurs pour chaque prototype est piquante et maladroite.
  3. Motif prototype (+1): tout est encapsulé dans un seul objet littéraire. Cela ressemble beaucoup au modèle de module.
  4. Modèle de constructeur (-1): il est non structuré et semble incohésif. Difficile de comprendre et de maintenir.

De plus, Haskell nous enseigne également une programmation fonctionnelle pure. Puisque les usines sont simplement des fonctions, nous pouvons call et apply usines, composer des usines, des usines de curry, des usines de mémoires, rendre les usines paresseuses en les soulevant et bien plus encore. Parce que new est un opérateur et non une fonction, vous ne pouvez pas le faire en utilisant de new . Oui, vous pouvez créer un équivalent fonctionnel de new mais alors pourquoi ne pas simplement utiliser des usines à la place? L'utilisation du new opérateur dans certains endroits et la new méthode dans d'autres endroits est incohérente.

3. Comment obtenons-nous le meilleur des deux mondes?

D'accord, les usines ont leurs avantages, mais encore la performance de Object.create suce, n'est-ce pas? Il le fait, et l'une des raisons en est que chaque fois que nous utilisons Object.create nous créons une nouvelle fonction de constructeur, définissons son prototype sur le prototype que nous voulons, instancions la fonction constructeur nouvellement créée à l'aide de new , puis renvoyez-la:

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

Peut-on faire mieux que cela? Essayons. Au lieu de créer un nouveau constructeur à chaque fois, pourquoi ne .constructor nous pas simplement la fonction .constructor du prototype donné?

 Object.create = function (o) { return new o.constructor; }; 

Cela fonctionne dans la plupart des cas, mais il y a quelques problèmes:

  1. Le prototype de o.constructor peut être différent de o .
  2. Nous voulons simplement construire une nouvelle instance de o , mais o.constructor peut avoir une logique d'initialisation qui ne peut pas se séparer de la construction.

La solution est assez simple:

 function defclass(prototype) { var constructor = function () {}; constructor.prototype = prototype; return constructor; } 

En utilisant defclass vous pouvez créer des classes comme suit:

 var Shape = defclass({ rectangle: function (p1, p2) { this.p1 = p1; this.p2 = p2; return this; }, circle: function (p, r) { this.p = p; this.r = r; return this; } }); var rectangle = (new Shape).rectangle(zeroPoint, Point.new(3, 4)); var circle = (new Shape).circle(origin, 3); 

Comme vous pouvez le voir, nous avons séparé la construction et l'initialisation et l'initialisation peut être reportée à plusieurs constructeurs. Il peut même être enchaîné comme suit: (new Shape).rectangle().circle() . Nous avons remplacé Object.create par un new qui est beaucoup plus rapide et nous avons toujours la flexibilité de faire tout ce que nous voulons. En outre, tout est bien encapsulé dans un seul objet littéraire.

Conclusion

Comme vous pouvez le constater, le new opérateur est un mal nécessaire. Si la new était une fonction d'usine, cela serait génial, mais il est mis en œuvre en tant qu'opérateur et les opérateurs en JavaScript ne sont pas de première classe. Cela rend plus difficile la programmation fonctionnelle avec de new . Les usines d'autre part sont flexibles. Vous pouvez adapter n'importe quel nombre de fonctions d'usine pour vos prototypes et la capacité de faire tout ce que vous voulez est le plus grand point de vente des fonctions d'usine.

En JavaScript, ce que les gens appellent l'héritage «pseudo-classique» est l' héritage prototypique. C'est le seul type d'héritage que JavaScript a eu. Éviter le new est comme éviter les instructions de switch parce que vous pouvez le faire en utilisant if/else if place. Bien sûr, vous pouvez, et parfois vous le devriez. D'autres fois, le switch est exactement le bon choix. Identique à new et Object.create : Utilisez le meilleur pour ce que vous faites.

Pour moi , c'est un peu subjectif (tout comme «l'héritage pseudo-classique est mauvais» meme, à mon avis):

  1. new est pour quand je fais des choses de classe. J'utilise les fonctions new et constructrices car elles correspondent bien à la façon dont la langue est conçue. (Oui, ce design est inhabituel, mais c'est ainsi). Donc, si je vais avoir des objets qui représenteront les gens et qui ont un comportement commun, je vais utiliser un constructeur Person , assigner des comportements (fonctions) à Person.prototype , Et utilisez une new Person pour les créer. (J'utilise mon script Lineage pour rendre cela plus concis, et pour traiter facilement certains éléments hiérarchiques.) C'est simple, familier, propre et clair: si vous voyez une new Person vous savez que je crée un nouvel objet. (Si je ne le suis pas – oui, il est possible de violer cette attente avec une fonction de constructeur – alors, à mon avis, je ne devrais pas écrire une fonction de constructeur en premier lieu.)

    Bien sûr, vous pouvez définir une fonction de createPerson ( createPerson , buildPerson , quoi que ce soit) qui fait la même chose en utilisant Object.create ou similaire. Je n'ai pas de problème avec les gens qui font cela si c'est ce qu'ils préfèrent (tant que le nom de la fonction est clair, il crée quelque chose). J'ai un problème avec les gens qui disent "vous ne devriez pas utiliser de new " comme si c'était un conseil objectif; C'est une opinion , c'est un conseil de style.

  2. Object.create est pour quand je fais du matériel au niveau de l'instance. Il y a un projet sur lequel je travaille, qui comporte un tas d'objets dans un arbre / graphique complexe. Ils sont uniquement des données, pas de comportement. Parfois, nous devons avoir des données qui ne sont pas encore vérifiées et ne doivent donc pas écraser les données précédentes. Donc, le conteneur a une référence aux données vérifiées ( verified ) et aux données non vérifiées (en current ). Pour éviter une ramification inutile dans le code, le conteneur a toujours les deux références, mais dans le cas normal, ils se réfèrent au même objet ( container.verified = container.current = {}; ). Presque tous les codes utilisent cet objet current car presque tous les codes devraient utiliser les informations les plus récentes. Si nous devons ajouter des données en attente, nous effectuons container.current = Object.create(container.verified); Puis ajoutez les données dans container.current . Étant donné que le prototype current est verified , il n'est pas nécessaire de copier toutes les anciennes données et d'avoir des données dupliquées partout. Par exemple, l'utilisation classique de la façade. new serait le mauvais outil pour ce travail, cela n'empêcherait pas.

L'une des nombreuses choses fantastiques sur JavaScript est que vous avez les deux options. J'utilise les deux dans les mêmes projets, pour différentes choses, tout le temps.

Merci pour votre réponse incroyable. Mais je ne suis pas d'accord avec la plupart de vos affirmations. Sans voir l'équivalent dans les deux modèles. Certains arguments sont subjectifs pour moi. Je voudrais donc me concentrer sur les faits. Et dans cette réponse, je vais commenter les meilleurs points de chacun. Et sans bibliothèques / extraits externes, alors nous pouvons nous battre pour savoir quelle bibliothèque est meilleure.

Bons points sur Object.create

1. Créez l'instance / objet en utilisant l'appel / appliquez

 var Shape = { Rectangle: function (p1, p2) { var rectangle = Object.create(this); rectangle.p1 = p1; rectangle.p2 = p2; return rectangle; }, Circle: function (p, r) { var circle = Object.create(this); circle.p = p; circle.r = r; return circle; } }; var rectangle = Shape.Rectangle.call(Shape, zeroPoint, Point.new(3, 4)); var circle = Shape.Circle.call(Shape, origin, 3); 

Ceci n'est pas possible en utilisant le new .

Bonnes remarques sur les new

1. Moins de code

 function Rectangle(p1, p2) { this.p1 = p1; this.p2 = p2; } function Circle(p, r) { this.p = p; this.r = r; } 

contre

 Rectangle: function (p1, p2) { var rectangle = Object.create(this); rectangle.p1 = p1; rectangle.p2 = p2; return rectangle; }, Circle: function (p, r) { var circle = Object.create(this); circle.p = p; circle.r = r; return circle; } 

Quoi qu'il en soit, vous devez toujours écrire plus de code pour mettre en œuvre la même chose.

2. Facilité de maintenance

 var rectangle = Shape.Rectangle(zeroPoint, Point.new(3, 4)); var circle = Shape.Circle(origin, 3); 

Que se passe-t-il si demain vous voulez changer le nom de Shape to Geometry? Vous devez passer en revue tout votre code et modifier tous les mots Shape pour chaque instanciation de cycle ou de rectangle. Ce point est plus remarquable lorsque vous faites des héritages. Parce que vous devez toujours appeler exactement le même nom du constructeur pour accéder aux supermarchés

Prime. Plus facile à lire ou à comprendre (Ceci est subjectif)

Si dans mon code je vois le new Rectangle(...) je sais que je crée une nouvelle instance de l'objet Rectangle. Cependant, Shape.Rectangle(...) ne me dit pas s'il s'agit d'un nouvel objet ou si c'est juste une fonction ou si Shape est une instance unique comme var Shape = new Whatever() .

3. Propriétés et méthodes privées

 var Person = function() { var a = 5; this.method = function (b) { return a*b; }; }; var obj = new Person; obj.method(5); // return 25 obj.a; // return undefined 

contre

 var Person = { a: 5, method: function (b) { return this.a*b; } }; var obj = Object.create(Person); obj.method(5); // return 25 obj.a; // return 5 

DdddddMéléldicedéldiceademetéletteachachachachetachachéesédachachéesédachachéesédachachéeséachachéeséeúdachachistánédachachéesédachachéesééachach Sur le motif object.create , vous pouvez si votre implémentation est spécifique. Si vous faites cela, votre code est plus difficile et plus bref d'écrire (mais il s'agit d'une opinion personnelle).

4. Je peux passer paramenter sur le constructeur

 var Person = function(a) { this.method = function (b) { return a*b; }; }; var obj = new Person(5); obj.method(5); // return 25 

contre

 var Person = { method: function (b) { return a*b; } }; var obj = Object.create(Person); obj.method(4); //Error 

5. instanceof

Aucune façon de faire instanceof nativement avec le motif Object.create .

6. Performance

Je laisse cela pour le dernier, car la plupart des autres points peuvent être résolus avec javascript supplémentaire. Mais dans ce cas, il ne peut pas être identique au new modèle. DdddddddMMMMMddMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM Dddddddddélbeélélélineélénélènesinasea aledédélagdéleterinas aleddégea hélagonnetáeaea aledéddélagdélachéleteráempaieé aleégie ale Mais si vous travaillez sur le backend et que vous faites une application très grande et évolutive, les performances sont importantes. Pourquoi Paypal quitte Java et passez à node.js? Parce que la performance est très importante dans les grands projets.

Conclusion

Donc, s'il est new il est 10 fois plus rapide que Object.create . Je ne pense que pour cette raison, et pour cette raison, il ne faut pas continuer à programmer avec de nouveaux. De plus, donnez-moi plus de souplesse et je peux faire des choses avec un new modèle que je ne peux pas avec Object.create . Et je suis d'accord que la nature du Javascript est d'utiliser Object.create . Marco illustrdGMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

Désolé pour mon anglais.