IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

L'opérateur new, bonne ou mauvaise pratique ? [Tutoriel]


Sujet :

JavaScript

  1. #1
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut L'opérateur new, bonne ou mauvaise pratique ?
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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
    https://www.youtube.com/watch?v=ya4UHuXNygM#t=3030



    Et vous ?

    Qu'en pensez-vous ? Doit-on bannir new et instanceof ?
    One Web to rule them all

  2. #2
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 093
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 093
    Points : 6 754
    Points
    6 754
    Par défaut
    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 - Visualiser dans une fenêtre à part
    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
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  3. #3
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Oui, tu dois parler de ce post ci

    Je suis d'accord avec toi, ce choix de paradigme apporte plus de problèmes qu'il n'en résout. Dans la vidéo, Douglas Crockford dit à juste titre que historiquement, JavaScript n'a jamais été fier de sa nature prototypale. Ça fait des décennies qu'on a le cul entre deux chaises, si vous me permettez l'expression. Je me demande bien pourquoi Object.create() est apparu après new, alors que ECMAScript est orienté prototypes depuis ses origines.
    One Web to rule them all

  4. #4
    Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2013
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2013
    Messages : 3
    Points : 4
    Points
    4
    Par défaut
    Je me demande bien pourquoi Object.create() est apparu après new, alors que ECMAScript est orienté prototypes depuis ses origines.
    ActionScript a pris la même direction. Il n'est plus maintenant basé sur les prototypes. J'espère que JavaScript prendra le même chemin ...

  5. #5
    Membre émérite
    Avatar de Kaamo
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    1 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 1 165
    Points : 2 778
    Points
    2 778
    Par défaut
    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

  6. #6
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    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 - Visualiser dans une fenêtre à part
    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
    +(new ((new Date()).constructor)()) == +(new Date()) //true
    One Web to rule them all

  7. #7
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 73
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Points : 22 933
    Points
    22 933
    Billets dans le blog
    125
    Par défaut
    Bonsoir

    Il est clair qu'il faut choisir et utiliser une seule méthode de travail.

    En parlant de création d'objet, de Object.create() et de Date :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /*
     * Object.create( proto [, propertiesObject ] ) 
     * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
     * 
     * On peut créer un objet valide de trois manières :
     */
    	var Obj1 = {},
    		Obj2 = new Object(),
    		Obj3 = Object.create( {} );
     
    	console.log( Obj1 instanceof Object ); // true
    	console.log( Obj1.constructor ); // function Object() 
     
    	console.log( Obj2 instanceof Object ); // true
    	console.log( Obj2.constructor ); // function Object()
     
    	console.log( Obj3 instanceof Object ); // true
    	console.log( Obj3.constructor ); // function Object() 
     
    /*
     * On peut créer un nouvel objet à partir du prototype d'un autre :
     */
    	var MonObjDate = Object.create( Date.prototype );
     
    	console.log( typeof MonObjDate ); // object
    	console.log( MonObjDate === Date ); // false
    	console.log( MonObjDate instanceof Date ); // true
    	console.log( MonObjDate.constructor ); // function Date() { [native code] }

    Blog

    Sans l'analyse et la conception, la programmation est l'art d'ajouter des bogues à un fichier texte vide.
    (Louis Srygley : Without requirements or design, programming is the art of adding bugs to an empty text file.)

  8. #8
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 093
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 093
    Points : 6 754
    Points
    6 754
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	console.log( Obj1.consructor ); // undefined
    Daniel, sérieusement, tu croyais nous avoir comme ça ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    console.log( Obj1.constructor ); // function Object()
    console.log( Obj2.constructor ); // function Object()
    console.log( Obj3.constructor ); // function Object()
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  9. #9
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 73
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Points : 22 933
    Points
    22 933
    Billets dans le blog
    125
    Par défaut

    Blog

    Sans l'analyse et la conception, la programmation est l'art d'ajouter des bogues à un fichier texte vide.
    (Louis Srygley : Without requirements or design, programming is the art of adding bugs to an empty text file.)

  10. #10
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Obj3 n'est pas strictement équivalent à Obj1 et Obj2, il faudrait écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Obj3 = Object.create(Object.prototype);
    // ou 
    Obj3 = Object.create(null);
    
    Object.getPrototypeOf({}) === Object.prototype // true
    Object.getPrototypeOf(Object.prototype) // null
    pour ne faire qu'une instanciation d'objet au lieu de deux.
    One Web to rule them all

  11. #11
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 73
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Points : 22 933
    Points
    22 933
    Billets dans le blog
    125
    Par défaut
    Oui !

    Blog

    Sans l'analyse et la conception, la programmation est l'art d'ajouter des bogues à un fichier texte vide.
    (Louis Srygley : Without requirements or design, programming is the art of adding bugs to an empty text file.)

  12. #12
    Membre émérite
    Avatar de Kaamo
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    1 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 1 165
    Points : 2 778
    Points
    2 778
    Par défaut
    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 )
    Excellent ! Je la note dans un coin

    Concernant Date, je ne crois pas que ça soit possible de l'étendre. C'est en dur dans le moteur. Par exemple dans V8 :
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    # Gets the value of a Date object. If arg is not a Date object
    # a type error is thrown.
    macro CHECK_DATE(arg) = if (%_ClassOf(arg) !== 'Date') ThrowDateTypeError();

    Donc tant que notre objet custom ne renverra pas "Date" explicitement quand on lui demander sa [[Class]], on ne pourra pas étendre les natifs :
    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    var MonObjDate = Object.create( Date.prototype );
    MonObjDate.getHours(); // TypeError: this is not a Date object.

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Object.prototype.toString.call(MonObjDate); // "[object Object]"
    Object.prototype.toString.call(new Date()); // "[object Date]"
    Object.prototype.toString.call( Date.prototype); // "[object Date]"

  13. #13
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Oui, Date est vraiment un des pires exemples possibles en matière d'API et d'implémentation...

    https://developer.mozilla.org/en-US/...l_Objects/Date

    Note that JavaScript Date objects can only be instantiated by calling JavaScript Date as a constructor: calling it as a regular function (i.e. without the new operator) will return a string rather than a Date object; unlike other JavaScript object types, JavaScript Date objects have no literal syntax.
    One Web to rule them all

  14. #14
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 73
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Points : 22 933
    Points
    22 933
    Billets dans le blog
    125
    Par défaut
    Date est très spécial en effet !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    console.log( Object.getPrototypeOf( Date ) ); // function Empty() {}

    Blog

    Sans l'analyse et la conception, la programmation est l'art d'ajouter des bogues à un fichier texte vide.
    (Louis Srygley : Without requirements or design, programming is the art of adding bugs to an empty text file.)

  15. #15
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Citation Envoyé par SylvainPV Voir le message
    Il ne faut pas confondre non plus l'instance et son constructeur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Object.getPrototypeOf(Berline); // function Empty() {}
    One Web to rule them all

  16. #16
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 73
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Points : 22 933
    Points
    22 933
    Billets dans le blog
    125
    Par défaut
    Oui ! À force de triturer la chose (instanciation, clonage ou clonage et extension), je mélange l'objet créé par new Date() et Date. Je renonce à trouver une solution.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    console.log( Date.prototype ); // Invalid Date

    Blog

    Sans l'analyse et la conception, la programmation est l'art d'ajouter des bogues à un fichier texte vide.
    (Louis Srygley : Without requirements or design, programming is the art of adding bugs to an empty text file.)

  17. #17
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    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 - Visualiser dans une fenêtre à part
    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
    One Web to rule them all

  18. #18
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    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 - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    function Model(arg){
        this.property = arg;
    }
     
    Model.prototype.method = function(){ ... };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    if(obj instanceof Model){ ... }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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.
    One Web to rule them all

  19. #19
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    Topic intéressant pour les débutants, cela devrait, en effet, leur clarifier les choses.

    En revanche, je trouve le constructeur de Crockford un peu tiré par les cheveux, alors qu'un truc aussi simple que ceci suffit amplement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var createConstructor;
     
    createConstructor = function createConstructor(construct) {
        var constructor;
     
        constructor = function constructor() {
            var instance;
     
            instance = Object.create(constructor.prototype);
     
            if (construct) {
                construct.apply(instance, arguments);
            }
     
            return instance;
        };
     
        return constructor;
    };
    Avec un petit exemple d'utilisation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    var construct,
        MyConstructor,
        instance,
        isMyConstructorInstance,
        inheritsFrom;
     
    construct = function construct() {
        console.log(arguments);
    };
     
    MyConstructor = createConstructor(construct);
     
    instance = MyConstructor(1, 2, 3); // [1, 2, 3]
     
    isMyConstructorInstance = function isMyConstructorInstance() {
        console.log(this instanceof MyConstructor);
    };
     
    inheritsFrom = function inheritsFrom(prototype) {
        console.log(Object.getPrototypeOf(this) === prototype);
    };
     
    MyConstructor.prototype.isMyConstructorInstance = isMyConstructorInstance;
    MyConstructor.prototype.inheritsFrom = inheritsFrom;
     
    instance.isMyConstructorInstance(); // true
    instance.isMyConstructorInstance(MyConstructor.prototype); // true
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  20. #20
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    En fait il a voulu faire une fonction tout-en-un pour déclarer à la fois l'objet parent, la fonction constructeur et les méthodes supplémentaires. Si tu retires la partie "méthodes", on arrive grosso-modo à ce que tu proposes.
    One Web to rule them all

Discussions similaires

  1. Surcharge de l'opérateur new
    Par :Bronsky: dans le forum C++
    Réponses: 17
    Dernier message: 27/10/2010, 21h33
  2. Grain de sel aléatoire, bonne ou mauvaise pratique ?
    Par Sergejack dans le forum Sécurité
    Réponses: 1
    Dernier message: 13/08/2009, 10h18
  3. Mauvaise pratique ? throw new exception()
    Par AsPrO dans le forum Langage
    Réponses: 4
    Dernier message: 24/04/2009, 11h36
  4. Redéfinition opérateurs new et delete globaux
    Par bolhrak dans le forum C++
    Réponses: 8
    Dernier message: 30/07/2007, 11h34
  5. namespace et opérateur new
    Par Sylvain Rousseau dans le forum C++
    Réponses: 3
    Dernier message: 06/01/2005, 23h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo