Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

JavaScript : L'opérateur new, bonne ou mauvaise pratique ?
Sylvain Pollet-Villard donne son opinion sur la question

Le , par SylvainPV

0PARTAGES

9  0 
L'opérateur new est-il une mauvaise pratique ?

On sait que l'opérateur new a été introduit en JavaScript pour rendre la programmation objet plus intuitive pour les développeurs accoutumés à la programmation objet orientée classes. La syntaxe est effectivement plus facile à prendre en main, mais présente aussi plusieurs inconvénients.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
function Car(model){
    this.model = model;
   this.speed = 0;
}

Car.prototype.wheels = 4;
Car.prototype.drive = function(){
    this.speed = 120;
}

var car = new Car("Ferrari");
car.drive();
console.log(car.speed); // 120

L'obligation d'avoir une fonction constructeur

En POO par classes, un constructeur est nécessaire pour passer du modèle à l'objet réel, de la classe à l'instance. En POO par prototypes, le modèle est un objet réel. Une fonction constructeur n'est donc pas obligatoire. C'est un petit plus qui nous sert à passer les propriétés initiales de l'objet en inline, et appeler éventuellement d'autres fonctions ou déclencher des évènements à la création de l'objet. Avec new, nous sommes forcés d'avoir un constructeur. Et ce que le développeur assimile à la classe est en fait la fonction constructeur, ce qui est très perturbant.

Code : Sélectionner tout
1
2
3
car.constructor === Car; // true

car instanceof Car; //true : si car est une instance de Car, alors Car est ma classe ? Non ! C'est le constructeur ! >_>
Et si je souhaite ajouter le constructeur de la voiture dans mon modèle Car ?

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
function Car(constructor, model){
   this.model = model;
   this.constructor = constructor;
   this.speed = 0;
}

var golf = new Car("Volkswagen","Golf");

golf.constructor === Car; // false !

L'héritage devient sans raison bien plus compliqué

C'est là que la plupart des développeurs Java sont complètement largués. En suivant l'approche new, un héritage se ferait de la sorte :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
function Berline(constructor, model){
    Car.apply(this, arguments); //équivalent de super();
}

Berline.prototype = new Car(); // le modèle de la classe fils est une instance de la classe parente ???

var volvo = new Berline("Volvo","S60");
Pour expliquer cela, il faut se rappeler que les prototypes sont des objets "réels", instanciés. Seulement, Car n'est pas le prototype, mais le constructeur du prototype ! Il ne faut pas confondre non plus l'instance et son constructeur.

Code : Sélectionner tout
1
2
3
4
Object.getPrototypeOf(volvo); // Car {model: undefined, constructor: undefined, speed: 0}
Object.getPrototypeOf(volvo) === Car //false
Object.getPrototypeOf(Berline); // function Empty() {}
Tout cela est vraiment perturbant, et source de fréquentes erreurs. Nous sommes contraints de garder une référence aux constructeurs, mais ceux-ci n'ont aucune utilité à part leur usage avec new.

Le contexte this fonctionne différemment

Avec new, this ne fonctionne plus de la même façon dans la fonction constructeur. Il ne fait pas référence au scope parent comme d'habitude, mais à l'instance nouvellement créée. C'est un comportement tout à fait spécial et là encore très perturbant. Cela a aussi comme grave conséquence de modifier le contexte parent par erreur si on oublie l'opérateur new à l'instanciation :

Code : Sélectionner tout
1
2
3
4
var toyota = Car("Toyota","Yaris"); //oups
console.log(window.model); // Yaris ! GLOBAL LEAK
console.log(window.constructor); // Toyota ! Aïe, j'ai même écrasé Window ! Bonjour les dégâts !

L'alternative : Object.create (support IE9+)

Voici un code similaire sans utiliser l'opérateur new :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* le prototype de base de tout objet est Object.prototype
   le prototype Car est un objet, on le crée via Object.create et on lui donne les propriétés qu'auront toutes les voitures */
var Car = Object.create(Object.prototype);
Car.wheels = 4;
Car.drive = function(){
   this.speed = 120;
};

// on crée une voiture à partir du prototype Car
var golf = Object.create(Car);
golf.constructor = "Volkswagen";
golf.model = "Golf";

Object.getPrototypeOf(golf) === Car; // true ! ENFIN

golf instanceof Car // TypeError: Expecting a function in instanceof check, but got #<Object> ; l'opérateur instanceof est à jeter avec new
Si on veut une fonction constructeur, on la déclare :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
Car.create = function(constructor, model ){
     return Object.create(Car, {
         constructor: { writable:true, configurable:true, value: constructor },
         model: { writable:true, configurable:true, value: model},
      });
};

var volvo = Car.create("Volvo","S60");
Notez les attributs writable, configurable... Il y en a d'autres, répertoriés ici : https://developer.mozilla.org/fr/doc...defineProperty
On peut paramétrer très précisément le comportement de la variable, et se rapprocher de la définition de variables privées ou semi-privées en surchargeant les getters et setters.

Le concept de l'héritage s'assimile très bien à celui de prototype :

Code : Sélectionner tout
1
2
3
4
var Berline = Object.create(Car);
var peugeot = Object.create(Berline);
console.log(peugeot.wheels); //4
Résumé des avantages :
  • libre d'utiliser une fonction constructeur ou d'attribuer les propriétés manuellement ;
  • plus d'utilisation hasardeuse du mot-clé this ;
  • on garde une référence au prototype et non plus au constructeur ;
  • l'héritage est aussi simple qu'il devrait l'être.


Pour ceux qui ne peuvent pas se passer de constructeurs et qui souhaitent éviter d'en définir un pour chaque objet, Douglas Crockford a travaillé sur une fonction qui remplace efficacement l'opérateur new sans perdre les avantages précités. La voici :


Douglas CrockFord définit son propre "new", septembre 2011



Et vous ?

Qu'en pensez-vous ? Doit-on bannir new et instanceof ?

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de Watilin
Expert éminent https://www.developpez.com
Le 21/05/2014 à 3:28
Si on essaye de penser avec sagesse et ne pas foncer tête baissée dans la nouveauté (comme j'ai l'habitude de faire ), on peut constater qu'il y a une équivalence assez forte entre le modèle actuel à base de new et de constructeurs, et le modèle « prototype seulement » qui est assez bien implanté aujourd'hui, et se dire que les deux sont bien, que ce n'est qu'une affaire de goût.

Code : Sélectionner tout
1
2
3
constructeur    <->  prototype
new             <->  Object.create
a instanceof b  <->  a.prototype === b
Les habitués de Java qui auront fait l'effort de s'adapter à l'étrange hybridation que fait JavaScript avec l'opérateur new n'ont peut-être pas envie d'avoir à apprendre un nouveau paradigme. Personnellement, je considère que cette possibilité de choisir son paradigme est une faiblesse du langage (c'est un peu du racolage et ça rend les interpréteurs compliqués), mais d'autres peuvent voir ça comme une force, une souplesse. Pour en rajouter une couche, il y a ES6 qui arrive et qui propose d'introduire les class. Quelqu'un sur DVP (c'était peut-être bien toi d'ailleurs) a dit il n'y a pas longtemps « il faut choisir un paradigme au début de son projet et s'y tenir, sinon ça peut vite devenir un joyeux bordel » ou quelque chose comme ça. Je prédis que pour les 10 prochaines années, les pires exemples de code spaghetti seront écrits en JavaScript

Même si tous les développeurs du Web se mettaient d'accord pour se débarasser de tel ou tel paradigme, dans le but de simplifier les interpréteurs et les rendre plus performants, ça ne serait pas possible car ça casserait la rétro-compatibilité. Cela dit, je dois avouer – attention opinion personnelle – que le paradigme « prototype seulement » me paraît merveilleusement simple et clair.

Peut-être qu'à l'avenir, les développeurs web pourront indiquer au début de leur script quel paradigme ils ont choisi, et l'interpréteur pourra basculer en « mode constructeur », en « mode prototype » ou en « mode classe » pour tourner plus efficacement. En fait, ça me fait mal à la tête d'essayer de comprendre comment un interpréteur actuel est capable de gérer 3 paradigmes différents
3  0 
Avatar de kaari kosaku
Membre habitué https://www.developpez.com
Le 27/11/2014 à 18:09
Personnellement, ayant commencé à toucher au JS en connaissant principalement des langages orientés objets, j'ai pris la fâcheuse habitude de me servir de "new" et de passer par des constructeurs.
Malgré tout, j'ai toujours trouvé que quelque chose ne tournait pas rond dans tout ça (new Date(), wtf ??), et je m'en suis rendu compte lorsque j'ai compris la "logique" de javascript, qui n'a rien à voir avec un langage de classes.

Alors après j'aurais bien envie de changer ma façon de coder pour mieux "coller" avec l'esprit js, mais "new" est tellement ancré dans mes habitudes que j'ai développé une peur profonde de ".prototype".

Je trouve qu'il est intéressant d'avoir permis cette façon de coder qui colle à la POO (et ça me colle littéralement à la peau...), mais je déplore que par ce biais, javascript en perde de sa contenance, ajoute un autre moyen de faire "la même chose" (pas très optimal selon moi...), et ajoute un argument de plus à la problématique "où va-t'on avec javascript ?"
3  0 
Avatar de lysandro
Membre averti https://www.developpez.com
Le 27/11/2014 à 12:41
-- new

Si on lit la spécification ES3, on s'aperçoit :
- que toutes les fonctions peuvent se comporter comme des constructeurs. Elles ont, associé, un prototype qui permet par exemple l'appel new ({}.toString)() que cela ait un sens ou non.
- qu'il y a une distinction sémantique entre appel comme constructeur et appel comme fonction, justement avec l'opérateur 'new'.
- qu'enfin, la spécification distingue explicitement ces différentes formes d'appel dans le cas des objets 'builtin' :
Constructeurs appelés comme fonction :
- Conversion de type : { Object, String, Boolean, Number }
- Appel comme constructeur : { Function, Array, Error }
- Autres :
-- Date([...]) <=> (new Date()).toString()
-- RegExp(<RegExp> <=> <RegExp>, et RegExp(...) <=> new Regexp(...)

Je n'arrive (vraiment) pas à comprendre, le problème avec 'new'. Je comprends bien que, si la fonction est écrite comme un constructeur, c'est à dire utilisant 'this' pour modifier l'objet, cela pose un problème si on oublie le 'new'. Mais :
1- c'est une erreur de programmation assez grossière, de débutant, un peu comme d'oublier un '=' dans un test d'égalité (peu de chance qu'un dev Java oublie 'new' !)
2- pourquoi avoir lié 'this' au contexte global hors de l'utilisation de 'new' ou d'un appel 'object.method()' où 'this' se comporte comme on peut l'attendre ?

Si vraiment c'est un gros problème, pourquoi diminuer la sémantique du langage en évitant 'new' ? Pourquoi au contraire ne pas en profiter pour proposer des constructeurs multi-fonctionnels ? ou à minima, gérer le problème
Code : Sélectionner tout
1
2
3
4
5
function Foo ( bar ) {
  if ( !(this instanceof Foo) )
    return new Foo(bar);
  this.bar = bar;
}
Comme ça, on vérouille le cas : var foo = Foo(...);, et on se retrouve dans le même cas que 'Function', 'Array', etc. Voire pour les plus angoissés une fonction encapsulant le constructeur :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
function Constructor ( Construct ) {
  return (function Ctor (/*arguments*/){
    if ( !(this instanceof Ctor) )
      return Ctor.apply(
          Object.create(Ctor.prototype),
          arguments );
    return Construct.apply(this, arguments);
  });
}
var Foo = Constructor(function( bar ) {
  this.bar = bar;
)};
// Foo(..) <=> new Foo(...)
On peut remarquer au passage qu'on traduit l'appel de fonction vers l'appel comme constructeur et que donc il vaut mieux utiliser 'new' : on évite ainsi un appel de fonction supplémentaire.

-- Object.create

C'est subjectif, mais je trouve le modèle d'héritage mis en place en Javascript assez élégant. La récursivité entre 'Object', 'Function' et leurs protoypes me rappelle (un peu) ObjVLisp.
En tout cas, on avait (pré ES5) un graphe d'héritage cohérent avec 'Object.prototype' comme racine (hors types primaires et objets externes).

ES5: introduction de 'Object.create' ... que permet cette fonction qu'on ne pouvait pas faire avant ? presque rien (je ne parle pas de 'Object.defineProperty') sauf ...
casser le graphe d'héritage. On peut créer des objets 'Object.create(null)', qui ne sont pas des types primaires, pas des objets externes mais qui se retrouvent hors du graphe d'héritage.
Sur le fond, pourquoi pas. Mais c'est quand même une rupture de taille. Avant d'appeler n'importe quelle methode de 'Object.prototype', il va falloir trouver un moyen de savoir si l'objet hérite ou pas de 'Object.prototype'. Quand on voit les acrobaties de code, ne serait-ce que pour savoir si un objet est un tableau ...
Si au moins on avait eu typeof object == "what do you mean exactly by 'typeof' ?".

Le coté positif, c'est que cela étend les capacités du langage.

-- D. Crockford

Sa fonction si je la comprends est une tentative à destination de (qui?) pour fournir un modèle de "classe" simplifié :

- possibilité d'hériter d'un constructeur (extend.prototype)
- séparation des attributs (initializer) et des méthodes (méthods)
- graphe d'héritage rendu statique au niveau des constructeurs (cloture du prototype)

C'est une approche. Je ne vois pas ce qu'elle apporte à part augmenter la confusion en créant des représentations qui ne sont pas celle du langage.
S'il avait conservé le 'new' cela aurait donné de la cohérence puisque qu'en général dans les langages de classe, 'new' est utilisé. Là au contraire, si on utilise 'new' on va forcer le langage a créer un objet, établir le lien d'héritage et tout mettre à la poubelle, puisque qu'on le snobe en refaisant le processus de l'autre coté ... Mais impossible de conserver 'new' puisque que justement, pour avoir un graphe d'héritage statique, le prototype est dans la cloture et que l'utilisation de 'new' risquerait de casser ce graphe d'héritage. Autrement dit, pour gérer une vision plus commune des langages de classes, il introduit lui même une rupture par rapport à ces langages.

La seule chose que je ne comprends pas c'est le test systèmatique de 'initializer' comme fonction ? comme il est dans la cloture il aurait pu optimiser ça.

Sur le fond, j'ai un peu de mal avec ses prises de position : il ne faut pas utiliser 'eval', 'switch', 'with', 'for(var', et je ne sais plus quoi encore. D'état d'esprit, je lui préfère Bjarne Stroustrup qui dit, en parlant du C++, quelque chose comme : utilisez n'importe quelle possibilité du langage tant que vous le faites à bon escient.

Quoi qu'il en soit, bon article qui a le mérite d'exposer un point de vue argumenté.
2  0 
Avatar de Watilin
Expert éminent https://www.developpez.com
Le 04/04/2018 à 1:35
Citation Envoyé par SylvainPV Voir le message
Personnellement j'aime bien mettre des propriétés propres sur tous les objets, y compris les prototypes. Je trouve ça plus simple à raisonner d'un point de vue "prototype", le prototype étant censé être un objet bien réel lui aussi. Cela permet aussi de pouvoir lister les propriétés qui seront partagées par tous les objets créés à partir de ce prototype. Je trouverais ça bizarre que le prototype Car n'ait aucune propriété propre, et que le seul moyen de déduire les propriétés communes serait de lire le code du constructeur.
Effectivement, pour le développement et la maintenance ça ajoute de la clarté, c’est une bonne chose.

Citation Envoyé par SylvainPV Voir le message
Typiquement, ce qui occupera le plus de place en RAM, ce ne sont pas les prototypes mais les instances dérivées, et celles-ci devront nécessairement avoir leurs propriétés propres.
Pour compléter tes propos, il peut être utile de savoir que les propriétés du proto sont masquées (shadowed) dès qu’on affecte une valeur à une instance. À la première écriture, la propriété de l’instance est créée, elle est distincte de la propriété du prototype, et le moteur JS gère deux valeurs complètement indépendantes.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var proto = {
  machin: "chose",
  changeMachin() { this.machin = "truc"; }
};

var objA = Object.create(proto);
console.log(objA.hasOwnProperty("machin")); // false

objA.changeMachin();
console.log(objA.hasOwnProperty("machin")); // true

// en accédant directement à la propriété, même résultat :

var objB = Object.create(proto);
console.log(objB.hasOwnProperty("machin")); // false

objB.machin = "bidule";
console.log(objB.hasOwnProperty("machin")); // true
On peut constater que la propriété existe toujours sur le proto et qu’elle n’a pas été modifiée&#8239;:

Code : Sélectionner tout
1
2
console.log(objA.machin); // "truc"
console.log(objA.__proto__.machin); // "chose"
Ainsi les propriétés du proto sont rarement utilisées, et on peut se servir de ce fait pour repérer certains oublis de programmation. Si une propriété d’une instance a la même valeur que celle du proto, ça signifie qu’elle n’a pas été initialisée.

Personnellement, je donne à mes prototypes des valeurs qui n’ont «&#8239;pas de sens&#8239;», par exemple NaN si la propriété est censée être un nombre, ou encore -1 si ça doit être un nombre positif.

Ça permet également de donner un indice sur le type (type hint), qui peut être utile à la fois au développeur ou à la développeuse qui lit le code, et aux optimiseurs de demain qui, on peut l’imaginer, sauront en tirer parti.
2  0 
Avatar de SylvainPV
Rédacteur/Modérateur https://www.developpez.com
Le 04/04/2018 à 21:33
Je crois que tu as déjà bien compris la différence entre les deux méthodes. Pour faire simple,
Code : Sélectionner tout
new Car(...args)
équivaut à
Code : Sélectionner tout
Car.call(Object.create(Car.prototype), ...args)
Je résume et reformule les deux arguments évoqués précédemment:
1) la facilité à raisonner: le prototype est censé jouer le rôle de "modèle" pour les autres instances créées, c'est donc plus simple à se représenter si le prototype est construit de la même façon que les instances (donc avec la même fonction constructeur appelée), et s'il dispose des mêmes propriétés propres que les instances (afin d'être pleinement substituable à une instance)
2) la réflectivité des propriétés communes: c'est intéressant de pouvoir déduire les propriétés communes d'objets dérivés d'un prototype en regardant directement dans le prototype avec Object.getOwnPropertyNames par exemple. Et il y a une grosse différence entre faire cette déduction par un développeur (en lisant un code) et l'obtenir directement de manière programmatique.

En fait même si la consommation mémoire supplémentaire avec new n'est pas forcément énorme cela me semble étrange car on peut lire ici et là qu'un des intérêts d'ajouter des propriétés au prototype du constructeur (plutôt que d'utiliser des this.quelqueChose dans ce constructeur) c'était une consommation mémoire plus faible puisque ces propriétés seront communes à/partagées par toutes les instances...


Bien sûr, hériter de la valeur du prototype permet d'économiser en mémoire, c'est d'ailleurs précisément ce à quoi servent les prototypes. Par exemple quand on veut des objets avec des méthodes en commun, on va les déclarer dans leur prototype pour éviter d'avoir X exemplaires de la même fonction en RAM. Mais ça n'a pas de sens d'hériter de tout, les objets ont nécessairement des propriétés propres sinon ils seraient tous identiques. Dans le cas présent, si on gère une liste de voitures, chaque voiture aura son propre modèle et son propre constructeur, cela n'a pas de sens de vouloir hériter cette valeur de la "voiture prototype". Par contre, Watlin suggère d'utiliser cette voiture prototype pour stocker des exemples / placeholders pour ces propriétés, comme par exemple { constructor: "Marque générique", model: "Modèle de base" }

Identifier les propriétés propres ou héritées se fait généralement assez naturellement. Tu peux te poser la question: Qu'est-ce qui est propre aux voitures en général ? 4 roues, une méthode drive(). Qu'est-ce qui est propre à cette voiture là en particulier ? Un modèle, une couleur, des options...
2  0 
Avatar de SylvainPV
Rédacteur/Modérateur https://www.developpez.com
Le 03/05/2018 à 19:50
A titre informatif, j'ai publié un article en anglais sur Medium qui reprend la plupart des points de cette actu, avec des détails et codes remis au goût du jour : https://medium.com/@SylvainPV/new-is...y-898b67ba7773
2  0 
Avatar de Kaamo
Membre émérite https://www.developpez.com
Le 21/05/2014 à 15:11
Tu sais que je suis d'accord avec toi Sylvain et que je préfère le pattern prototype Object.create au pattern constructor new Function.
Mais ... le new, et donc constructor, est trop ancré dans le paysage de JavaScript pour ne pas l'utiliser en production.

En gros, je pense que tant qu'on est dans la norme ES5, il faut utiliser new en production. A l'heure actuelle, les interpréteurs sont taillés pour gérer des instances créées à partir de constructor.

Les avantages de new pour créer des instances :
- Les moteurs JavaScript du marché accèdent à leur propriétés très rapidement grâce notamment aux hidden class
- ça entre mieux dans le paysage de JavaScript (pour créer des instances, autre que passer par la forme littérale, il faut utiliser new. Ex : new Date(), new Boolean(), etc. et non date = Object.create(Date))
- Dans le prochain ES6, il y a les class qui débarquent. Celles-ci utiliseront les constructeurs.

On a beau vouloir évangéliser les débutants, je pense que les constructeurs font partie intégrante, et inhérente, à JavaScript (du moins pour ES5). Faut donc être conscient des dangers.
Mais sur un projet, je n'ai jamais vu un lead-dev prendre la décision d'utiliser le pattern Object.create. Les débutants seraient trop perdus malheureusement.
On est donc coincé avec ce foutu new
1  0 
Avatar de SylvainPV
Rédacteur/Modérateur https://www.developpez.com
Le 21/05/2014 à 17:43
Bien sûr, je n’espérais pas arrêter d'utiliser new du jour au lendemain. C'est comme un vieux chewing-gum collé sous une semelle, on l'a depuis des mois mais on a jamais le courage de s'arrêter pour le gratter. Et plus le temps passe, plus il est difficile à décoller... Alors on apprend à vivre avec, on commence à s'y attacher, et ça se termine par l'ajout de class en ES6 (oui je trolle )

Ça fait un petit moment que je voulais mettre par écrit quelques codes et arguments expliquant pourquoi, selon moi, Object.create est bien moins perturbant à utiliser que new. Si les débutants devraient se sentir perdus, c'est pour de mauvaises raisons ; principalement tous les tutos, codes existants et certaines API dont Date qui sont basés sur les constructeurs. Et il y a encore et toujours la lutte des paradigmes (et non pas la lutte des classes, n'est-ce pas Marx) qui ne réussit pas trop aux prototypes, la loi de la majorité l'emportant sur le reste.

En parlant de Date :
Code : Sélectionner tout
1
2
3
4
5
Date() == new Date() // true
+(Date()) == +(new Date()) //false
(new Date()).constructor === Date //true
new ((new Date()).constructor)() == new Date() //false
&#8203;+(new ((new Date()).constructor)()) == +(new Date()) //true
1  0 
Avatar de SylvainPV
Rédacteur/Modérateur https://www.developpez.com
Le 23/05/2014 à 10:59
Il n'y a pas de solution, il faut soit définir son propre objet Date, soit créer un objet wrapper :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var MyDate = (function() {   
   var native = new Date();
   var wrapper = Object.create(null);
   Object.getOwnPropertyNames(Date.prototype).forEach(function(prop){
      wrapper[prop] = function(){ 
         return native[prop].apply(native, arguments);
      };
   });
   return wrapper;
})();

MyDate.getNextYear = function(){ 
   return this.getFullYear() + 1;
};

var d = Object.create(MyDate);
d.getFullYear(); // 2014
d.getNextYear(); // 2015
1  0 
Avatar de SylvainPV
Rédacteur/Modérateur https://www.developpez.com
Le 27/05/2014 à 3:20
J'ai réécrit le code du projet sur lequel je travaille actuellement pour migrer de new vers Object.create.
Premier constat, ça s'est fait rapidement et sans trop de heurts. Le projet était totalement testé unitairement, ce qui a facilité la migration.
Deuxio, certains patterns revenaient souvent et j'ai pris rapidement quelques réflexes :
- remplacer les fonctions constructeurs par une fonction create:

Code : Sélectionner tout
1
2
3
4
5
6
function Model(arg){
    this.property = arg;
}

Model.prototype.method = function(){ ... };
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
var Model = Object.create({
  create: function(arg){
    var model = Object.create(Model );
      model.property = arg;
      return model;
    },
    method: function(){ ... }
});


Le code est un poil plus verbeux, mais avoir le constructeur et les méthodes déclarés au même niveau dans le même objet est plutôt plaisant. Cela permet également d'appeler plusieurs fois la fonction constructeur sur une même instance, et d'éviter tous les problèmes exposés dans la premier post lorsqu'on oublie l'opérateur new.

- remplacer les tests d'instance avec instanceof par isPrototypeOf

Code : Sélectionner tout
if(obj instanceof Model){ ... }
Code : Sélectionner tout
if(Model.isPrototypeOf(obj){ ... }
L'ordre des arguments est inversé, mais ça ne change pas grand chose question lisibilité/praticité.

- remplacer le passage de fonctions par référence par de l'héritage (lexical scoping --> prototypes)

Code : Sélectionner tout
1
2
3
4
5
function methodeCommune(){ ... }

ModelA.prototype.method = methodeCommune;
(...)
ModelB.prototype.method = methodeCommune;
Je me servais souvent de variables locales au scope pour partager des méthodes et variables entre plusieurs modèles, étant donné que faire de l'héritage avec new est très fastidieux. Bien sûr, ce n'est pas l'idéal : il faut un scope pour ranger les références, on est obligés de déclarer les modèles au même endroit, et non pas dans des fichiers différents etc... Avec le passage à Object.create, j'ai supprimé toutes ces références et repensé mes modèles pour qu'ils héritent d'un prototype commun. Parfois, ce prototype parent est très simple et ne sert qu'une seule fois, mais la syntaxe est tellement facile à utiliser que ça ne rend pas le code plus compliqué, au contraire.

J'ai gardé l'usage de new avec les objets standards, ceux déjà déclarés dans le navigateur.
1  0