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. #61
    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
    Ce n'était pas une critique, simplement je pense que cette question n'a pas autant d'importance que ce que tu veux lui en prêter. La différence en conso mémoire est anecdotique et l'usage est quasiment inchangé, donc c'est surtout une préférence personnelle. J'ai essayé de te répondre avec le maximum de détails et d'argumentation, et je ne vois pas quoi ajouter de plus.

    Citation Envoyé par Beginner. Voir le message
    ce que j'aime bien dans les constructeurs ou les pattern ou les modules (je m'y perd donc je te laisse comprendre...) c'est par exemple que c'est facile d'initialiser des données simplement avec en passant des arguments, d'avoir des propriétés publics et privées... Je ne vois pas comment on fait ça juste avec Object.create...
    J'ai donné un exemple dans le post initial:
    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");
    Là où avec new tu es obligé d'avoir un et un seul constructeur, tu es libre de créer autant de fonctions usines (factory functions) que tu as besoin ; et en prime tu peux configurer les propriétés de façon plus avancée sans avoir à passer par Object.defineProperty
    One Web to rule them all

  2. #62
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 874
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 874
    Points : 3 721
    Points
    3 721
    Par défaut
    Au fait pour info, j'ai vu sur le site : https://developer.mozilla.org/fr/doc.../Object/create un exemple qui utilise justement la méthode trois que j'ai ré-expliquée au message #58 :


    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
    29
    30
    31
    // Forme, la classe parente
    function Forme() {
      this.x = 0;
      this.y = 0;
    }
     
    // Méthode de la classe parente
    Forme.prototype.déplacer = function(x, y) {
      this.x += x;
      this.y += y;
      console.info('Forme déplacée.');
    };
     
    // Rectangle - classe fille
    function Rectangle() {
      // on appelle le constructeur parent
      Forme.call(this);
    }
     
    // La classe fille surcharge la classe parente
    Rectangle.prototype = Object.create(Forme.prototype);
    Rectangle.prototype.constructor = Rectangle;
     
    var rect = new Rectangle();
     
    console.log('instance de Rectangle ? ', (rect instanceof Rectangle));
    // true
    console.log('une instance de Forme ? ', (rect instanceof Forme));
     // true 
    rect.déplacer(1, 1);
    // Affiche 'Forme déplacée.'
    Ils utilisent Rectangle.prototype = Object.create(Forme.prototype); plutôt que Rectangle.prototype = new Forme(); même si le bénéfice n'est pas énorme pourquoi s'en priver vu que pour l'obtenir ça coûte pratiquement rien ?

  3. #63
    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
    Beginner.,

    il n’y a de magie ni derrière Object.create ni derrière new. Je comprends ton envie de comprendre, car comprendre, c’est dissiper la magie, et nous développeurs et développeuses, la magie on n’aime pas trop ça.

    Je vais essayer de mettre en lumière les différences entre create et new, pour montrer qu’elles ne sont au final que de petits détails.

    Première différence, comme tu l’as bien compris et comme Sylvain l’a dit, Object.create ne souffre point de constructeur.

    Si tu veux appliquer un « constructeur » sur un objet créé avec create, tu peux déclarer une fonction et décréter qu’elle fera office de constructeur pour tes objets. On pourrait appeler ça un « initialiseur ».

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var proto = { description: "prototype de base" };
     
    function initialize(obj, randomProperty, uselessProperty) {
      obj.randomProperty  = randomProperty;
      obj.uselessProperty = uselessProperty;
    }
     
    var instance = Object.create(proto);
    initialize(instance, "bazar", 6846);

    J’aurais pu utiliser this et call comme Sylvain l’a fait…
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Car.call(Object.create(Car.prototype), ...args);
    … Mais quand je fais ça, mes amis me jettent des pierres au visage alors j’essaye de perdre cette habitude.

    Non, plus sérieusement, je vais donner des arguments : le fait que this puisse être changé dynamiquement en JS rend le code moins prévisible, car on n’est jamais sûr de la valeur qu’aura this à l’exécution. Utiliser this c’est prendre le risque d’embrouiller les débutants et débutantes, qui ne maîtrisent pas forcément la mécanique de call, apply et bind, et qui risqueraient de ne pas saisir comment tu veux que ton code soit utilisé.

    Également, il y a le risque de pollution de l’espace global si tu n’y prends pas garde. Je ne sais plus si ça a déjà été discuté sur ce fil, mais rapidement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    function constructSomething() {
      this.someProp = "squalala";
    }
     
    // oups, j’ai oublié call
    constructSomething(myObj);
    Comme il n’y a pas "use strict", on est en mode sloppy et this référence window. Ce qui vient de se passer, c’est qu’on a ajouté someProp à window… Pas terrible.
    En mode strict, this est undefined et on reçoit une erreur. C’est un peu mieux, mais si on ne connaît pas le truc de call, ça ne nous aide pas beaucoup.

    Oh tiens, une remarque comme ça : tu t’es déjà demandé ce qui se passe quand tu repasses un objet à son propre constructeur ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function Bidule(a, b) {
      this.a = a;
      this.b = b;
    }
     
    var bidule = new Bidule(23, 98);
    Bidule.call(bidule, 66, 34);
    La réponse est : rien de spécial. Je l’ai déjà dit dans un autre post, le constructeur ne fait rien de plus que ce que tu as écrit dedans. Il n’y a pas de magie !

    Bref, je me suis un peu égaré, revenons au sujet. Je vais tenter d’énumérer les différences de comportement entre un objet créé avec create et un objet créé avec new.

    • new renseigne automatiquement la propriété constructor (qui n’est pas fiable car pas en lecture seule, mais passons) ;
    • instanceof attend une fonction comme opérande de droite, on ne peut pas l’utiliser si on n’a pas de constructeur ;
    • les interpréteurs / compilateurs savent peut-être (*) mieux optimiser la forme new que la forme create, mais ça c’est susceptible d’évoluer dans le temps.


    (*) J’ai lu que c’était le cas pour V8 en particulier, mais c’était il y a un moment déjà, ce n’est peut-être plus d’actualité. Je ne retrouve plus la source, désolé :S

    Peut-on émuler ces comportements à la main ? On va essayer.

    D’abord il nous faut un prototype :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    var proto = { description: "un prototype de base" };
    Puis un « initialiseur ». Dans cet initialiseur on va s’occuper de lier constructor.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function Init(obj, propA, propB) {
      obj.propA = propA;
      obj.propB = propB;
      obj.constructor = Init;
    }
    Note que j’ai repris, par habitude, la convention de mettre une capitale comme avec les constructeurs de style « new ».

    Rien de surprenant jusqu’ici, mais faisons quand même un premier test.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var obj1 = Object.create(proto);
    Init(obj1, 42, "banane");
    console.log(obj1.propA); // 42
    console.log(obj1.description); // "un prototype de base"
    console.log(obj1.constructor); // function Init()
    Tout se passe comme prévu.

    Comment recoller les morceaux avec instanceof ? Déjà, regardons exactement comment fonctionne cet opérateur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var machin = {};
    var obj = Object.create(machin);
    function randomFunc() {}
     
    console.log(obj instanceof randomFunc); // false, évidemment
     
    randomFunc.prototype = machin;
     
    console.log(obj instanceof randomFunc); // true
    instanceof regarde uniquement si la propriété prototype de la fonction donnée se trouve dans la chaîne des prototypes de l’objet.

    Note : toute fonction a une propriété propre prototype, j’en conclus qu’elle la reçoit du… Constructeur Function. (Ce qui nous amène à la question « j’ai des actions chez Doliprane » : pourquoi le prototype des fonctions n’a-t-il pas de propriété prototype ?)

    On peut également faire la bidouille dans l’autre sens :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function f(){}
    f.prototype = { a : 1 };
     
    var o = Object.create({});
     
    console.log(o instanceof f); // false
     
    Object.setPrototypeOf(o, f.prototype);
    console.log(o instanceof f); // true
    Je ne vais pas illustrer le cas où il y a plusieurs niveaux d’héritage, mais c’est la même chose. instanceof remonte la chaîne de prototypes, ça le rend capable de détecter le type d’un objet polymorphe. Par exemple, on peut avoir un objet qui renvoie true à la fois pour obj instanceof A et pour obj instanceof B, parce qu’il hérite de A et que A hérite de B.

    Il y a un truc important à constater : dans mes tentatives ci-dessus pour émuler avec create le comportement exact des objets de style « new », j’ai dû écrire plusieurs fois uneFonction.prototype = unObjet;. On voit ressurgir la syntaxe utilisée pour les prototypes du style new.

    Vu sous un autre angle : si on met de côté constructor (qui n’est pas fiable de toute façon), avec le style new, il faut déjà attacher à la main le prototype à une fonction pour que les choses fonctionnent. Ce qui me fait dire que les constructeurs, c’est vraiment surcoté.

    Le manque de souplesse d’instanceof peut être contourné en utilisant la méthode .isPrototypeOf() qui, elle, n’exige pas qu’on lui passe une fonction. Je n’en ai pas parlé avant, mais .isPrototypeOf() fonctionne avec les deux styles.

    Bon, avant de m’éloigner encore du sujet, voilà ma conclusion : new et create sont quasiment équivalents en termes de sémantique (encore une fois, si on oublie la propriété constructor). Avec new, il est nécessaire d’attacher un prototype au constructeur ; avec create, un constructeur n’est pas obligatoire, mais si on en veut un il faut y rattacher le prototype. C’est symétrique.

    Un peu de lecture : https://gist.github.com/ericelliott/...541d0ed365f5fd

    Non, c’est pas vrai, je n’ai pas d’actions chez Doliprane.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  4. #64
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 874
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 874
    Points : 3 721
    Points
    3 721
    Par défaut
    Merci à vos deux pour vos réponses et vos efforts...
    Bon je vous lâche avec cette question (méthode 1 Vs méthode 3)...

    1- Par contre le message de Watilin m'a refait penser à une autre question : que pensez-vous des fonctions qui retournent un objet comme moyen de fabriquer des objets en initialisant éventuellement certaines valeurs avec les paramètres passés en argument ??? Là on n'utilise ni new ni Object.creat mais j'ai pourtant vu cela ici et là dans mes lectures...

    On voit aussi qu'avec une IIFE on peut fabriquer des objets alors je pose la même question que ma question 1 mais pour les IIFE...

    Il y a aussi plusieurs livres, articles, tutos qui en parlent plus ou moins de cette manière...


    Merci.

  5. #65
    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 déplacé la discussion sur les propriétés privées dans ce topic : https://www.developpez.net/forums/d1.../#post10145066 ; Essayons de garder 1 topic = 1 sujet

    des fonctions qui retournent un objet comme moyen de fabriquer des objets en initialisant éventuellement certaines valeurs avec les paramètres passés en argument
    Ce sont les fonctions usines que j'ai mentionné précédemment. Pas besoin de prototype ou de new pour construire des objets à partir de paramètres.
    One Web to rule them all

  6. #66
    Membre éclairé
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    335
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 335
    Points : 715
    Points
    715
    Par défaut
    Bonjour,
    Je n'ai eu le temps de lire que certains passages, aussi je fais quelques remarques qui ont peut-être déjà été formulées.

    Si je reprends le premier message :
    En définissant "Car" directement comme prototype, ne perd-on pas la possibilité de faire des méthodes statiques ? (Sans entrer dans le débat des méthodes statiques qui sont souvent une solution de facilité et ne sont pas très orientées objet dans le fond.)

    Justement ici :
    Citation Envoyé par SylvainPV Voir le message
    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");
    (Une remarque préliminaire : Car.create construit en effet l'objet, mais, à mon sens, en général quand on parle de fonction constructeur, il s'agit de la fonction appelée pour initialiser l'objet quand on le crée et non pour le créer à elle seule.)

    Car.create n'est pas tout à fait comparable à Objet.create dans la mesure où cette dernière est dans l'esprit une méthode statique.
    Car.create devient une méthode accessible directement par les instances, on pourrait donc créer une voiture à partir d'une autre.
    On pourrait considérer qu'elle pollue un peu l'ensemble des méthodes des instances.

    (J'ajoute (14h20) : je ne veux pas dire que Car.create ne peut pas être utilisée de façon statique, mais qu'on ne retrouve pas la particularité qu'a une fonction statique de ne pas être accessible directement depuis les instances.)

    Citation Envoyé par SylvainPV Voir le message
    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)
    Concrètement on peut le voir ainsi, mais moi je retiens plus l'idée derrière, la mienne en tout cas, qui est de faire une classe dérivée.
    Bien souvent, le traitement effectué dans le constructeur ne concerne en rien le prototype intermédiaire, on peut même avoir certains dysfonctionnements à l'appeler sur un prototype.

    Citation Envoyé par SylvainPV Voir le message
    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.
    Réflectivité ou réflexivité ?

    Dans l'exemple de la partie "L'alternative : Object.create" du message 1, "wheels" sera une propriété propre du prototype "Car", et non du prototype "Berline".
    (J'ajoute (12h55) : en relisant l'exemple, j'imagine que vous pourriez avoir l'inverse avec la propriété "model" en créant "Berline" avec Car.create.)

    Or, avec l'exemple de Beginner. et sa méthode 1, on aurait l'inverse :
    Car.prototype.model n'existe pas à la différence de Berline.prototype.model.
    (Là bien sûr il est question des prototypes de "Car" et "Berline", qui ne sont plus directement des prototypes.)
    Berline.prototype aurait des propriétés propres ne faisant pas partie de Car.prototype, ce qui est un peu gênant dans la mesure où elles ont été définies depuis la classe "Car"...

    Après, pourquoi pas, mais personnellement, les trois approches que je vais privilégier au niveau des propriétés (autres que les méthodes) définies sur le prototype sont :
    - Soit aucune.
    - Soit certaines : par exemple des propriétés dont la valeur par défaut suffit, beaucoup d'instances auront la même.
    Je trouve également pratique, à défaut d'être clair et recommandable, de faire un delete d'une propriété propre d'un objet pour "retomber" sur la valeur par défaut.
    - Soit toutes, mais sans répétition inutile au niveau des sous-classes, donc le même principe que les méthodes, exemple :
    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
    function Car(constructor,model)
    	{
    	this.model=model;
    	this.constructor=constructor;
    	}
    Car.prototype.model=undefined; //ou null ou ""
    Car.prototype.constructor=undefined;
    Car.prototype.speed=0;
    Car.prototype.optionGPS=false;
    Car.prototype.GPS=undefined;
     
    function BerlineLuxe(constructor,model,GPS)
    	{
    	Car.call(this,constructor,model);
    	this.GPS=GPS;
    	}
    BerlineLuxe.prototype=Object.create(Car.prototype);
    BerlineLuxe.prototype.optionGPS=true; //nouvelle valeur par défaut
    BerlineLuxe.prototype.proprieteSpecifiqueAuxBerlines=undefined; //ne pas utiliser dans les méthodes de Car.
    Attention aux valeurs "object" :
    On pourrait être tenté parfois de mettre ce genre de choses Car.prototype.pressionPneus={avant:0,arriere:0}; et faire directement voiture.pressionPneus.avant=2; sans avoir créé une nouvelle instance "pressionPneus".
    Cela modifierait la valeur pour toutes les instances de "BerlineLuxe".

    Citation Envoyé par Beginner. Voir le message
    est-ce que le mieux pour cette personne c'est d'utiliser la méthode 1 ou bien la méthode 3 ?
    Pour moi, la 3.

    Citation Envoyé par Watilin Voir le message
    le moteur crée une instance de Car en copiant les propriétés (ce qui inclut les méthodes) du prototype, c’est la partie .create() ;
    Les propriétés ne sont pas copiées justement, non ?

  7. #67
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 874
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 874
    Points : 3 721
    Points
    3 721
    Par défaut
    Salut,

    Citation Envoyé par Loralina Voir le message
    Pour moi, la 3.
    Merci pour votre réponse. Je ne sais pas si certains utilisent cette méthode 3 (il y a quand même un exemple sur le site MDN que j'ai posté au message #62 et j'ai remarqué (cf. message #55) que les objets créés avec les class ES6 semblent plus proches de ceux crées avec la méthode 3 que de ceux crées avec la méthode 1) mais j'ai cru comprendre que Object.create a été "implémenté" (je ne sais si c'est le bon mot) après new donc avant on ne pouvait pas utiliser la méthode 3 mais on pouvait utiliser la méthode 1 ce qui expliquerait peut-être que ce soit celle-là qu'on voit (plus souvent) dans certains livres, tutos, articles ???

    Bon après (si on met de coté la duplication* des propriétés et le fait que ce soit peut-être plus rapide puisque qu'on évite l’exécution* du constructeur) c'est peut-être juste une question de goût (puisque d'un point de vu fonctionnel il n'y a pas vraiment de différence) ???

    * Pour un niveau d'héritage on a un facteur deux... Je n'ai pas testé mais si on a plus d'un niveau d'héritage le facteur sera d’autant plus grand ???

    Enfin bref...

    Citation Envoyé par Loralina Voir le message
    Citation Envoyé par Watilin Voir le message
    le moteur crée une instance de Car en copiant les propriétés (ce qui inclut les méthodes) du prototype, c’est la partie .create() ;
    Les propriétés ne sont pas copiées justement, non ?
    Ah vous avez l’œil, oui moi aussi j'avais tilté sur ce point mais je n'ai pas relevé car je me suis dit que Watilin ne voulait pas vraiment dire qu'il y a une copie des propriétés mais une copie de la référence à l'objet "Car.prototype"...

    Ce que j'ai compris c'est que Object.create(Car.prototype) revient à créer un objet "vide" (newObj) puis à affecter au proto de cet objet la référence à l'objet "Car.prototype" ---> var newObj = {}; newObj.__proto__ = Car.prototype; ou var newObj = {}; Object.setPrototypeOf(newObj, Car.prototype);...

    C'est donc la référence à l'objet "Car.prototype" qui est copiée ce qui explique pourquoi toute modification de l'objet "Car.prototype" se répercute sur toutes les instances de Car et ce même si la modification intervient après l'instanciation (de ces instances de Car)... Ainsi les propriétés de l'objet "Car.prototype" sont communes à (partagées par) toutes les instances de Car (ces instances n'ont donc pas chacune une copie de ces propriétés ce qui permet une économie de mémoire).

    On nous conseille d’ailleurs d'ajouter nos méthodes au prototype du constructeur car ainsi elles seront communes à (partagées par) toutes les instances, on évite ainsi que chaque instance ait ses propres copies, un des intérêts évoqués étant une économie de mémoire... C'est aussi à cause de ça (économie de mémoire) que je m'interrogeais sur la question méthode 1 Vs méthode 3...

    Bon après il est possible que j'ai mal compris...

  8. #68
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 874
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 874
    Points : 3 721
    Points
    3 721
    Par défaut
    Citation Envoyé par SylvainPV Voir le message
    Ce sont les fonctions usines que j'ai mentionné précédemment. Pas besoin de prototype ou de new pour construire des objets à partir de paramètres.
    Merci. Oui j'avais bien compris qu'on n'avait "Pas besoin de prototype ou de new pour construire des objets à partir de paramètres" mais je voulais savoir la place qu'ont les fonctions usines par rapport au sujet du fil (en gros "constructeurs + new" Vs "seulement Object.create")...

    - Est-ce qu'on peut utiliser les fonctions usines avec l'une des deux autres méthodes ou bien il faut choisir l'une des trois méthodes/moyens de fabriquer des objets ?

    - Fait-on aussi de l'héritage avec les fonctions usines ?

    - Est-ce que les modules ES6 sont des alternatives aux fonctions usines ?

    Je pose ces questions à tous et bien sûr personne n'est obligé de me répondre...

    Merci.

  9. #69
    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
    Citation Envoyé par Loralina Voir le message
    Citation Envoyé par Watilin Voir le message
    le moteur crée une instance de Car en copiant les propriétés (ce qui inclut les méthodes) du prototype, c’est la partie .create() ;
    Les propriétés ne sont pas copiées justement, non ?
    En effet, elles ne sont pas copiées. Ni avec new, ni avec create. Ce que je voulais dire, c’est que l’objet reçoit sa liaison avec son prototype à ce moment. On peut imaginer qu’en interne, le moteur appelle setPrototypeOf ou bien affecte la propriété __proto__.

    Un peu hors sujet : sur la page de doc que je viens de lier, je vois qu’il est conseillé d’ajouter des propriétés à monConstructeur.prototype plutôt que d’y affecter un nouvel objet, pour raison de performance. Ça me revient maintenant, j’ai vu quelque fois des gens utiliser une méthode extend, venant de jQuery ou d’une autre bibli, pour initialiser le prototype de leurs constructeurs.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function Licorne() {
      ...
    }
     
    maBibli.extend(Licorne.prototype, {
      methode: function () { ... },
      ...
    });
    Citation Envoyé par Beginner. Voir le message
    et j'ai remarqué (cf. message #55) que les objets créés avec les class ES6 semblent plus proches de ceux crées avec la méthode 3 que de ceux crées avec la méthode 1)
    Que veux-tu dire par « semblent plus proches » ? J’ai essayé de t’expliquer dans mon précédent post que les différences étaient minimes et qu’il n’y avait rien de magique.
    Si on a un objet donné et qu’on essaye de déterminer s’il a été créé par la méthode 1 ou 2, le seul fait sur lequel on peut se baser, c’est la présence ou non d’une propriété constructor. Et comme on l’a vu, ce n’est pas fiable, car on peut modifier nous-mêmes cette propriété, que ce soit sur l’instance ou sur le prototype. Quant aux propriétés propres, elles ont pu être affectées par le constructeur ou par n’importe quoi d’autre. C’est pour ça que j’insiste tant sur le fait que la seule vraie différence, c’est la syntaxe.

    Et du coup, la question de la méthode 3 (mélanger les méthodes 1 et 2) n’a plus vraiment de sens si on admet que les méthodes 1 et 2 sont équivalentes, du point de vue du résultat.

    Quant aux classes ES6, elles sont sémantiquement équivalentes à la méthode 1 (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
    18
    19
    class CochonDInde {
      couiner() { console.log("couii"); }
    }
    console.log(typeof CochonDInde); // "function"
     
    var thierry = new CochonDInde();
    console.log(CochonDInde.constructor); // function CochonDInde()
    console.log(thierry instanceof CochonDInde); // true
     
    thierry.couiner(); // "couii"
    thierry.hasOwnProperty("couiner"); // false
     
    Object.getPrototypeOf(thierry);
    /* Object {
      constructor: function CochonDInde()
      couiner: function couiner()
      __proto__: Object { … }
    }
    */
    Erratum : dans mon post précédent, j’ai dit qu’une fonction constructeur affectait automatiquement la propriété constructor. Ce n’est pas vrai : en réalité, la propriété appartient au prototype. Si on regarde de plus près, le prototype possède dès le départ cette propriété, à la création de la fonction.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object.getOwnPropertyNames(( function(){} ).prototype); // Array [ "constructor" ]
    Je crois que ça donne un début de réponse à ma question Doliprane, car si le prototype des fonctions avait une propriété prototype, à quoi ferait référence la propriété constructor de ce dernier ? Mais je m’égare…

    Tiens d’ailleurs, je reviens sur l’histoire avec extend ci-dessus. Si on n’utilise pas extend, on écrase le prototype, et avec lui constructor. Comme en général, on passe un objet littéral, notre instance récupère la propriété constructor de celui-ci, qui est Object.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function T(){}
    T.prototype = {}; // ici on écrase le prototype
    var t = new T();
    console.log(t.constructor); // function Object()
    C’est vrai aussi pour Object.create({}). En revanche, Object.create(null) n’a pas de constructeur, car il n’a pas de prototype. C’est, à mes yeux, la seule vraie différence entre new et create : create permet de créer des objets sans prototype.

    mais j'ai cru comprendre que Object.create a été "implémenté" (je ne sais si c'est le bon mot) après new donc avant on ne pouvait pas utiliser la méthode 3 mais on pouvait utiliser la méthode 1 ce qui expliquerait peut-être que ce soit celle-là qu'on voit (plus souvent) dans certains livres, tutos, articles ???
    Tout à fait. Douglas Crockford en parle dans son article prototypal inheritance. Le MDN précise que Object.create a été implémenté avec JavaScript 1.8.5, ce qui, en ce qui concerne Firefox, nous ramène aux alentours de juillet 2010. Le standard ECMAScript 5.1, qui définit Object.create, a été finalisé en juin 2011. Pour rappel, JavaScript est né en 1995.

    Bon après il est possible que j'ai mal compris...
    Si tu ne l’as pas encore lu, peut-être que l’article héritage et chaîne de prototypes te sera utile

    Citation Envoyé par Beginner. Voir le message
    Merci. Oui j'avais bien compris qu'on n'avait "Pas besoin de prototype ou de new pour construire des objets à partir de paramètres" mais je voulais savoir la place qu'ont les fonctions usines par rapport au sujet du fil (en gros "constructeurs + new" Vs "seulement Object.create")...

    - Est-ce qu'on peut utiliser les fonctions usines avec l'une des deux autres méthodes ou bien il faut choisir l'une des trois méthodes/moyens de fabriquer des objets ?

    - Fait-on aussi de l'héritage avec les fonctions usines ?

    - Est-ce que les modules ES6 sont des alternatives aux fonctions usines ?

    Je pose ces questions à tous et bien sûr personne n'est obligé de me répondre...

    Merci.
    – Une usine (ne pas confondre avec « fabrique » qui est un patron de conception) est avant tout une boîte noire, qui te permet de masquer les détails d’implémentation de l’instanciation d’un objet. À l’intérieur tu peux faire ce que tu veux, c’est une fonction comme les autres. Certains constructeurs t’obligent à utiliser new (la plupart viennent du DOM, par exemple Blob) ; d’autres te l’interdisent (Symbol) ; comme tu peux le voir il y a tous les cas de figures. Un des avantages des usines c’est d’uniformiser tout ça.

    – Pour faire de l’héritage, tu peux faire appel à d’autres usines… Ou pas. Encore une fois, tu peux faire ce que tu veux. Mais c’est mieux de n’utiliser qu’une seule façon de faire pour garder un code cohérent.

    – Quant aux modules ES6, leur rôle est d’aider à gérer les relations entre différents fichiers de script, je ne vois pas le rapport avec l’instanciation d’objets.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  10. #70
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 874
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 874
    Points : 3 721
    Points
    3 721
    Par défaut
    Merci Watilin,

    Citation Envoyé par Watilin Voir le message
    On peut imaginer qu’en interne, le moteur appelle setPrototypeOf ou bien affecte la propriété __proto__.
    Oui c'est aussi comme ça que je l'imagine c'est pourquoi j'avais écris (regarde juste après la flèche "--->") :

    Citation Envoyé par Beginner. Voir le message
    Ce que j'ai compris c'est que Object.create(Car.prototype) revient à créer un objet "vide" (newObj) puis à affecter au proto de cet objet la référence à l'objet "Car.prototype" ---> var newObj = {}; newObj.__proto__ = Car.prototype; ou var newObj = {}; Object.setPrototypeOf(newObj, Car.prototype);...
    mais c'est juste pour faire simple (comme faire un dessin) car en fait j'ai vu* (mais je ne sais pas si c'est vraiment le cas) que Object.create() utiliserait new, c'est marrant car finalement on l’utiliserait indirectement ce new, on ne l'éviterait pas totalement...

    * Dans le lien prototypal inheritance que tu as donné je vois ça :

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    if (typeof Object.create !== 'function') {
        Object.create = function (o) {
            function F() {}
            F.prototype = o;
            return new F();
        };
    }
    newObject = Object.create(oldObject);

    On voit l’usage de new... Je ne sais pas si elle était définie comme ça à l'époque mais aujourd'hui ça ne doit pas être ça puisque Object.create() peut avoir un deuxième argument mais dans le polyfill proposé ici qui tient compte du deuxième argument optionnel on voit aussi l'usage de new...

    Après c'est peut-être new qui utilise setPrototypeOf...

    Citation Envoyé par Watilin Voir le message

    Un peu hors sujet : sur la page de doc que je viens de lier, je vois qu’il est conseillé d’ajouter des propriétés à monConstructeur.prototype plutôt que d’y affecter un nouvel objet, pour raison de performance. Ça me revient maintenant, j’ai vu quelque fois des gens utiliser une méthode extend, venant de jQuery ou d’une autre bibli, pour initialiser le prototype de leurs constructeurs.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function Licorne() {
      ...
    }
     
    maBibli.extend(Licorne.prototype, {
      methode: function () { ... },
      ...
    });
    Ah j'avais vu ça aussi mais je ne savais pas que c'était pour une question de performance... Je pensais qu'on passait par une bibliothèque car cette méthode n'existait pas en natif...

    Mais maintenant on utiliserait plutôt Object.assign(), non ? Dans ton exemple on remplacerait maBibli.extend(Licorne.prototype,... par Object.assign(Licorne.prototype,... ???


    Citation Envoyé par Beginner. Voir le message
    Bon après il est possible que j'ai mal compris...
    Citation Envoyé par Watilin Voir le message
    Si tu ne l’as pas encore lu, peut-être que l’article héritage et chaîne de prototypes te sera utile
    Dois-je en déduire que j'ai mal compris ? Si toi ou quelqu'un d'autre voyez des erreurs dans mon explication n'hésitez pas à les signaler...

    Sinon oui j'avais vu ce lien mais merci quand même car cela me fera un rappel...


    ------------------

    Je répondrais à la suite plus tard...

  11. #71
    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 Watilin Voir le message
    En revanche, Object.create(null) n’a pas de constructeur, car il n’a pas de prototype. C’est, à mes yeux, la seule vraie différence entre new et create : create permet de créer des objets sans prototype.
    Techniquement, tous les objets en JS ont un prototype. Si on remonte la chaîne prototypale de n'importe quel objet, on tombera toujours sur null à la fin. null étant une primitive et non un objet, il n'a pas de prototype, cependant il est tout à fait correct de dire que null est le prototype d'autres objets tels que Object.prototype. Mais des objets sans prototype, ça n'existe pas.
    One Web to rule them all

  12. #72
    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
    Merci Sylvain pour cette correction.

    En tout cas, ce que je trouve épatant avec Object.create(null), c’est que ça nous permet de créer un objet qui n’hérite pas de Object, ce qui nous donne la liberté de créer une nouvelle « branche » d’objets complètement indépendante de la branche native. Je ne sais pas si beaucoup d’autres langages nous permettent de faire ça.

    Après tout, pourquoi devrait-on avoir d’office, sur chacun de nos objets, une quinzaine de méthodes qu’on utilise rarement, alors qu’elles pourraient être des fonctions statiques de Object ?
    Pourquoi doit-on écrire ({ reponse: 42 }).hasOwnProperty("reponse") et non pas Object.hasOwnProperty({ reponse: 42 }, "reponse") ? Encore une incohérence de JS à mon avis.

    Citation Envoyé par Beginner. Voir le message
    * Dans le lien prototypal inheritance que tu as donné je vois ça :

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    if (typeof Object.create !== 'function') {
        Object.create = function (o) {
            function F() {}
            F.prototype = o;
            return new F();
        };
    }
    newObject = Object.create(oldObject);

    On voit l’usage de new... Je ne sais pas si elle était définie comme ça à l'époque […]
    L’article date de 2008, c’est-à-dire avant l’apparition de create ! Ce que propose Douglas Crockford, c’est en fait un polyfill. Le create qu’on a aujourd’hui donne des objets qui n’ont pas de propriété constructor, j’en conclus qu’il ne fait pas intervenir de fonction intermédiaire, et donc n’utilise pas new.

    Après c'est peut-être new qui utilise setPrototypeOf...
    Probablement. Quelque chose comme Object.setPrototypeOf(instance, monConstructeur.prototype);.

    Mais maintenant on utiliserait plutôt Object.assign(), non ? Dans ton exemple on remplacerait maBibli.extend(Licorne.prototype,... par Object.assign(Licorne.prototype,... ???
    C’est une question que je me suis posée aussi, mais il est indiqué dans la doc que assign ne copie que les propriétés propres. Si tu veux faire de l’héritage à plusieurs niveaux, seules les propriétés du dernier niveau seront copiées.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var protoA = { x: 3, y: 5 };
     
    var objA = Object.create(protoA);
    objA.z = -2;
     
    function B () {}
    Object.assign(B.prototype, objA);
     
    objB = new B();
     
    console.log(objB.z); // -2
    console.log(objB.x); // undefined
    La seule chose qui manque en fait, c’est la référence au prototype du niveau supérieur. Il suffit de rajouter cette instruction, avant ou après assign :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object.setPrototypeOf(B.prototype, protoA);
    (Ou Object.setPrototypeOf(B.prototype, Object.getPrototypeOf(objA)); si on n’a pas de référence sur le prototype.)
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  13. #73
    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
    Pour l'anecdote, Object.create(null) était très utile pour gérer des dictionnaires avant l'arrivée des Map en ES6. Comme ces objets n'avaient aucune propriété héritée, on éliminait le risque de collision.
    One Web to rule them all

  14. #74
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 874
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 874
    Points : 3 721
    Points
    3 721
    Par défaut
    Dans la pratique vous utilisez quoi comme moyen de fabriquer des objets, de faire de l'héritage...

    Avez-vous une préférence ?

    Les class ES6 ça vous dit rien si j'ai bien compris ?

    Personnellement je suis partagé, je n'ai pas assez de connaissances et de pratique pour savoir quoi choisir, j'ai changé d'avis plusieurs fois...

    Après je ressent une chose même si une méthode est meilleure on ne l'utilisera pas forcément parce qu'elle n'est pas (encore) assez répandue et que du coup elle est moins documentée que les autres...

  15. #75
    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
    Pour être honnête, ça dépend des projets.

    Dans mes projets personnels, les plus récents utilisent Object.create.*Les autres utilisent les constructeurs. Il y a bien eu un projet où j’ai commencé avec class, mais ne voyant pas l’avantage par rapport aux constructeurs classiques, je l’ai rapidement réécrit avec create avant qu’il devienne trop difficile à faire évoluer.

    Dans les projets tiers, c’est évidemment hors de question d’imposer son style de codage aux autres, du coup je m’adapte.

    La technique create est bien supportée et bien documentée, il n’y a pas de risque à l’utiliser, crois-moi. Une partie de la communauté l’a adoptée car elle est bien plus cohérente avec l’esprit du langage.

    Citation Envoyé par Beginner. Voir le message
    En particulier la phrase "Il crée un objet avec une méthode get_status et une propriété privée status" est vraiment étonnante car il me semble que si il est vrai que la méthode get_status est une propriété de l'objet retourné par la fonction quo eh bien ce n'est pas le cas de status. Et un peu plus loin on a : "La méthode get_status possède toujours un accès privilégié à la propriété status de quo..." donc pour lui status appartient à qui ? A l'objet retourné par la fonction quo ou à fonction quo ? Ce n'est pas clair...
    Par souci de commodité je remets le code ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var quo = function (status) {
        return {
            get_status: function (  ) {
                return status;
            }
        };
    };
     
    // Créer une instance de quo.
     
    var myQuo = quo("amazed");
     
    document.writeln(myQuo.get_status(  ));
    Pour commencer, il faut savoir que JavaScript stocke de la même façon les arguments et les variables locales. D’un point de vue portée, elles sont équivalentes.

    Au moment où l’objet { get_status: function ( ) {} } est retourné, il « sort » de la portée. C’est toujours quand quelque chose s’échappe de sa portée (via return ou en affectant une variable d’une portée supérieure) qu’il y a création d’une fermeture.

    Pour répondre à ta question, status appartient à la portée qui a été « sauvegardée » par la création de la fermeture. Quand j’ai commencé à comprendre le concept de fermeture, j’aimais bien visualiser une sorte « d’objet invisible » dont les propriétés seraient les variables de la portée, et vers lequel l’objet myQuo aurait une référence tout aussi invisible.
    En l’occurence, myQuo est la seule chose qui a connaissance de cette fermeture, on peut donc dire que les variables qui s’y trouvent « appartiennent » à l’objet, en quelque sorte.

    J’imagine que Douglas n’a pas voulu perdre le lecteur en commençant à expliquer le concept des fermetures à cet endroit du livre, du coup il a préféré faire une approximation.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  16. #76
    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
    Ma préférence, je l'ai exposée en ouvrant ce topic. Mais ça reste une préférence personnelle, et la plupart des devs n'ont pas le luxe d'orienter un code selon leurs préférences personnelles, car c'est dans la plupart des cas un travail d'équipe. Dans les faits, j'utilise un peu de tout selon les choix faits pour chaque projet et les facteurs externes (styleguide des frameworks, paramétrage des linters, navigateurs à supporter etc.). Le plus important, c'est de comprendre comment fonctionne chacun de ces patterns, plutôt que les reproduire sans réfléchir.
    One Web to rule them all

  17. #77
    Membre éclairé
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    335
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 335
    Points : 715
    Points
    715
    Par défaut
    Bonsoir,
    Citation Envoyé par SylvainPV Voir le message
    Pour l'anecdote, Object.create(null) était très utile pour gérer des dictionnaires avant l'arrivée des Map en ES6. Comme ces objets n'avaient aucune propriété héritée, on éliminait le risque de collision.
    C'est justement un exemple du lien mis par Watilin.
    J'ai remarqué ce petit détail en testant (en rappelant toutefois que l'utilisation de __proto__ n'est pas recommandée) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var dict={};
    dict.__proto__=null;
    console.log(dict.__proto__,Object.getPrototypeOf(dict)); //undefined, null
    dict.__proto__=null; //crée une propriété "__proto__"
    console.log(dict.__proto__); //null
    La première fois, __proto__ étant sous la forme d'un accesseur, il n'y a pas d'affectation directe.
    Vu que le prototype est mis à null, il n'y a plus de référence à __proto__.

    Citation Envoyé par Watilin Voir le message
    sur la page de doc que je viens de lier, je vois qu’il est conseillé d’ajouter des propriétés à monConstructeur.prototype plutôt que d’y affecter un nouvel objet, pour raison de performance.
    J'ai compris différemment à moins qu'on ne se réfère pas à la même chose (vous corrigerez si c'est moi qui me trompe) :
    Une petite précision d'abord :
    Quand on parle du prototype de quelque chose, on peut parfois se demander si on parle de la propriété prototype de ce quelque chose ou de sa propriété __proto__.
    Par exemple, dans mon message précédent, quand j'ai dit "des prototypes de "Car" et "Berline"", je parlais de leur propriété prototype.

    Dans l'avertissement, il est pour moi question de la modification de la propriété __proto__ ("[[prototype]]").
    (J'ajoute (8h09) : disons qu'il s'agit de modification "en apparence" de cette propriété qui est en fait un accesseur/mutateur comme j'ai dit au-dessus.)
    Il n'y a rien de dit à cet endroit concernant la modification de la propriété prototype comme on le ferait dans cet exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function f1(){}
    function f2(){}
    var p=f2.prototype;
    f2.prototype=Object.create(f1.prototype); //est-ce coûteux ou déconseillé de modifier le prototype ?
    console.log(f2.prototype===p); //false
    Citation Envoyé par Watilin Voir le message
    Ça me revient maintenant, j’ai vu quelque fois des gens utiliser une méthode extend, venant de jQuery ou d’une autre bibli, pour initialiser le prototype de leurs constructeurs.
    Cela est sans doute intéressant pour faire de l'héritage multiple (un peu dans le même esprit que les "traits").
    Je trouve ça un peu dommage de l'utiliser dans le cas d'un héritage simple car on va dupliquer les propriétés (même si les fonctions restent en commun).
    On se retrouve avec des propriétés propres sur le prototype (j'ajoute (8h09) : je veux dire que certaines propriétés pourraient ne figurer que sur la classe parente).
    Je conçois que cela peut convenir à certains (je pense que c'est un peu ce que disait SylvainPV concernant la réflexivité sur les propriétés).

  18. #78
    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
    Réfléxivité* alors

    Oui attention, comme la propriété "prototype" du constructeur correspond au prototype des instances construites, on a tendance à parler du "prototype de Car", mais il ne s'agit pas du prototype du constructeur lui-même. Les prototypes des constructeurs sont généralement Function.prototype, et on en a aucune utilité. A ajouter à la liste des confusions apportées par les constructeurs

    Je reviens sur une de tes remarques, Loralina:
    Bien souvent, le traitement effectué dans le constructeur ne concerne en rien le prototype intermédiaire, on peut même avoir certains dysfonctionnements à l'appeler sur un prototype.


    C'est précisément sur ce point que les avis divergent.
    D'une part, sur la manière dont on conçoit l'héritage, car certains pensent en termes d'interface tandis que d'autres pensent en terme de comportement. Il y a eu beaucoup de discussions à ce sujet lors de l'introduction du mot-clé super en ES6.
    Et d'autre part, sur ce qui distingue la POO prototypes de la POO par classes: la POO par prototypes met fin à la dichotomie classe/instance, pour unifier en un seul concept: l'objet ; tout objet ayant un prototype et tout objet pouvant être le prototype d'un autre objet. Conséquemment, un
    objet dérivé d'un prototype doit être parfaitement substituable à ce prototype dans un code donné (sur un principe similaire à celui de Liskov). Et donc il ne devrait pas y avoir de dysfonctionnements à invoquer le constructeur parent, si cette logique est respectée.
    One Web to rule them all

  19. #79
    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
    Citation Envoyé par Loralina Voir le message
    J'ai remarqué ce petit détail en testant (en rappelant toutefois que l'utilisation de __proto__ n'est pas recommandée) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var dict={};
    dict.__proto__=null;
    console.log(dict.__proto__,Object.getPrototypeOf(dict)); //undefined, null
    dict.__proto__=null; //crée une propriété "__proto__"
    console.log(dict.__proto__); //null
    La première fois, __proto__ étant sous la forme d'un accesseur, il n'y a pas d'affectation directe.
    Vu que le prototype est mis à null, il n'y a plus de référence à __proto__.
    Intéressant

    Citation Envoyé par Loralina Voir le message
    Dans l'avertissement, il est pour moi question de la modification de la propriété __proto__ ("[[prototype]]").
    (J'ajoute (8h09) : disons qu'il s'agit de modification "en apparence" de cette propriété qui est en fait un accesseur/mutateur comme j'ai dit au-dessus.)
    Il n'y a rien de dit à cet endroit concernant la modification de la propriété prototype comme on le ferait dans cet exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function f1(){}
    function f2(){}
    var p=f2.prototype;
    f2.prototype=Object.create(f1.prototype); //est-ce coûteux ou déconseillé de modifier le prototype ?
    console.log(f2.prototype===p); //false
    C’est une excellente remarque, je n’avais pas fait attention à ça. Mais note que la doc parle de la propriété interne [[Prototype]]. Pour moi il s’agit d’une chose qui est gérée par le moteur JS et qui n’est pas exposée au développeur. Et si je ne dis pas de bêtise, la propriété constructeur.prototype et l’accesseur-mutateur instance.__proto__ font tous les deux référence à cette propriété interne.

    Edit : la doc met en garde sur la « modification » d’un prototype, c’est assez vague quand on y pense. Est-ce que ça concerne uniquement la réaffectation d’un prototype, ou bien également le simple fait d’ajouter des propriétés dessus ? Si c’est le second cas, alors mes réflexions autour de extend sont inutiles

    Citation Envoyé par Loralina Voir le message
    Je trouve ça un peu dommage de l'utiliser dans le cas d'un héritage simple car on va dupliquer les propriétés (même si les fonctions restent en commun).
    Dupliquer, c’est justement le but quand on utilise extend ou assign. Mais il n’y a pas de différence de traitement entre les propriétés et les méthodes. Prenons un exemple avec cet objet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    var monObjet = {
      unePropriete: "It don’t mean a thing",
      uneMethode: function () { console.log("It you ain’t got that swing"); }
    };
    (Par la suite je vais dire « propriété » indifféremment pour les propriétés et pour les méthodes.)

    Je peux l’attacher avec une affectation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monConstructeur.prototype = monObjet;
    Ou bien par copie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    maBibli.extend(monConstructeur.prototype, monObjet);
    • Dans le premier cas, le prototype est écrasé ; dans le second, on le conserve et on y ajoute des propriétés ;
    • Dans le premier cas, on passe une référence directe à monObjet, ce qui économise un peu de mémoire ; dans le second cas, on copie ses propriétés vers le prototype.


    La mémoire économisée est de l’ordre de la taille de monObjet ; on peut raisonnablement considérer ça comme négligeable. Rappelons-nous qu’on copie vers un prototype, et que les propriétés seront partagées entre les instances, il n’y a pas de duplication supplémentaire.

    Citation Envoyé par Loralina Voir le message
    On se retrouve avec des propriétés propres sur le prototype (j'ajoute (8h09) : je veux dire que certaines propriétés pourraient ne figurer que sur la classe parente).
    Je conçois que cela peut convenir à certains (je pense que c'est un peu ce que disait SylvainPV concernant la réflexivité sur les propriétés).
    Je ne sais pas si j’ai bien compris ce que tu veux dire : tu considères l’existence d’un proto qui n’aurait que des propriétés héritées ? C’est possible, mais je ne vois pas l’intérêt. Ça fait un étage « creux » dans la hiérarchie.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var monInstance = Object.create( Object.create(monObjet) );
    /*
      monInstance
          |
          | getPrototypeOf()
          |
          \---------> { } // étage inutile
                       |
                       | getPrototypeOf()
                       |
                       \----------> monObjet { unePropriete, uneMethode }
    */
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  20. #80
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 874
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 874
    Points : 3 721
    Points
    3 721
    Par défaut
    Salut, (oui je sais encore moi...)

    Citation Envoyé par Loralina Voir le message
    J'ai compris différemment à moins qu'on ne se réfère pas à la même chose (vous corrigerez si c'est moi qui me trompe) :
    ...
    Dans l'avertissement, il est pour moi question de la modification de la propriété __proto__ ("[[prototype]]").[/CODE]
    Merci pour cette précision. Oui moi aussi c'est comme cela que je l'ai compris mais c'était en lisant un autre lien du même site à savoir : Object.prototype.__proto__ et aussi en lisant ce fil : __proto__ VS. prototype in JavaScript dans lequel est donné un lien vers cet article intéressant : JavaScript. The Core.

    Du coup je ne vois pas ce qui serait faux dans ce que j'avais écrit :

    Citation Envoyé par Beginner. Voir le message
    Ce que j'ai compris c'est que Object.create(Car.prototype) revient à créer un objet "vide" (newObj) puis à affecter au proto de cet objet la référence à l'objet "Car.prototype" ---> var newObj = {}; newObj.__proto__ = Car.prototype; ou var newObj = {}; Object.setPrototypeOf(newObj, Car.prototype);...

    C'est donc la référence à l'objet "Car.prototype" qui est copiée ce qui explique pourquoi toute modification de l'objet "Car.prototype" se répercute sur toutes les instances de Car et ce même si la modification intervient après l'instanciation (de ces instances de Car)... Ainsi les propriétés de l'objet "Car.prototype" sont communes à (partagées par) toutes les instances de Car (ces instances n'ont donc pas chacune une copie de ces propriétés ce qui permet une économie de mémoire).

    On nous conseille d’ailleurs d'ajouter nos méthodes au prototype du constructeur car ainsi elles seront communes à (partagées par) toutes les instances, on évite ainsi que chaque instance ait ses propres copies, un des intérêts évoqués étant une économie de mémoire... C'est aussi à cause de ça (économie de mémoire) que je m'interrogeais sur la question méthode 1 Vs méthode 3...
    Mais bien entendu comme vous l'avez rappeler, il est déconseillé de modifier directement la propriété __proto__ ("[[prototype]]"), il est conseillé d'utiliser plutôt Object.create()...

    Citation Envoyé par Loralina Voir le message
    Une petite précision d'abord :
    Quand on parle du prototype de quelque chose, on peut parfois se demander si on parle de la propriété prototype de ce quelque chose ou de sa propriété __proto__.
    Par exemple, dans mon message précédent, quand j'ai dit "des prototypes de "Car" et "Berline"", je parlais de leur propriété prototype.
    Oui c'est parfois l'embrouille je propose quelque chose que j'essayais déjà de faire à savoir utiliser dans nos messages le mot proto (ou [[prototype]] comme suggéré dans dans certains articles) quand on veut parler de la propriété interne __proto__ qu'ont (tous ? ***) les objets et le mot "prototype" pour parler de la propriété qu'ont les fonctions (c'est seulement les fonctions qui ont cette propriété, non ?).

    D'ailleurs cette propriété "prototype" qu'ont les fonctions a elle-même une propriété interne __proto__, j'ai ajouté à votre exemple de code cette ligne : console.log(f2.prototype.__proto__=== f1.prototype); et j'obtiens true...

    *** Sauf peut-être les objets crées avec Object.create(null); ?

    -------
    Citation Envoyé par Watilin Voir le message
    Mais note que la doc parle de la propriété interne [[Prototype]]. Pour moi il s’agit d’une chose qui est gérée par le moteur JS et qui n’est pas exposée au développeur
    Ben autrefois peut-être qu'elle n'était pas exposée mais maintenant c'est le cas apparemment, on la voit dans la console du débogueur, on peut la "lire" et "l'affecter"... (je l'ai d'ailleurs fait plusieurs fois dans mes testes) :

    Attention : Bien que la propriété Object.prototype.__proto__ soit déjà supportée dans la plupart des navigateurs à l'heure actuelle, son comportement n'a été standardisé que récemment avec la spécification ECMAScript 2015. Si vous avez besoin d'utiliser cette propriété dans des environnements antérieurs à ES2015, il est recommandé d'utiliser Object.getPrototypeOf().

    La propriété __proto__ de Object.prototype est une propriété accesseur (un couple de fonction avec un accesseur (getter) et un mutateur (setter)) qui expose le [[Prototype]] interne (qui est soit un objet, soit null) de l'objet courant.

    L'utilisation de __proto__ est sujet à controverse. Elle a été déconseillée par plusieurs personnes et n'avait jamais été incluse dans la spécification ECMAScript. Cependant, de nombreux navigateurs ont décidé de l'implémenter. À l'heure actuelle, la propriété __proto__ a été standardisée avec la spécification ECMAScript 2015 et sera officiellement supportée à l'avenir. Une alternative à cette propriété peut être l'utilisation des méthodes Object.getPrototypeOf/Reflect.getPrototypeOf et Object.setPrototypeOf/Reflect.setPrototypeOf. Cependant, modifier le [[Prototype]] d'un objet est toujours une opération lente qui doit être évitée le plus possible pour des raisons de performances.
    Mais si j'ai mal compris ce que tu a voulu dire n'hésite pas à me corriger...

    Citation Envoyé par Watilin Voir le message
    La mémoire économisée est de l’ordre de la taille de monObjet ; on peut raisonnablement considérer ça comme négligeable. Rappelons-nous qu’on copie vers un prototype, et que les propriétés seront partagées entre les instances, il n’y a pas de duplication supplémentaire.
    Justement il peut aussi y avoir une duplication de propriété dans les instances selon la méthode d’instanciation utilisée, c'est ce que j’essayais d’expliquer (sans grand sucés apparemment) quand je comparais la méthode 1 avec la méthode 3 (et celle avec les class ES6)... D'un point de vue fonctionnel c'était quasiment pareil mais avec la méthode 1 on avait une duplication de propriété dans les instances...

    EDIT : Je précise (pour éviter d'embrouiller) que je ne voulais pas parler de l'exemple que tu donnes même si je cite ce passage...

    Citation Envoyé par Watilin Voir le message
    ...
    Ou bien par copie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    maBibli.extend(monConstructeur, monObjet);
    • Dans le premier cas, le prototype est écrasé ; dans le second, on le conserve et on y ajoute des propriétés ;
    • Dans le premier cas, on passe une référence directe à monObjet, ce qui économise un peu de mémoire ; dans le second cas, on copie ses propriétés vers le prototype.
    Je ne suis pas sûr de comprendre à moins que tu voulais écrire maBibli.extend(monConstructeur.prototype, monObjet); à la place de maBibli.extend(monConstructeur, monObjet); ?
    ...

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