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. #81
    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 Beginner. Voir le message
    Citation Envoyé par Watilin
    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"...
    […]
    Mais si j'ai mal compris ce que tu a voulu dire n'hésite pas à me corriger...
    Non c’est moi qui ai parlé trop vite, je voulais parler du concept de « propriété interne » de manière générale. Effectivement celle-ci est exposée, mais quand on utilise le terme « propriété interne », on veut bien sûr parler d’un truc qui n’est pas censé être directement accessible en temps normal.

    D’ailleurs, si je voulais chipoter je dirais que la propriété interne [[Prototype]] nous est toujours inaccessible, c’est simplement que __proto__ en est une « vue ». Il ne serait pas surprenant, si on regardait dans le code source de V8 ou de Spidermonkey, de voir que le proto interne a d’autres propriétés que nous ne voyons jamais et que le moteur utilise pour sa gestion interne.

    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...
    Ce n’est pas clair, il va falloir que tu réexpliques
    Attention cependant, dans les exemples que tu as montrés tu avais un constructeur qui faisait des choses aussi. Vérifie que ces duplications dont tu parles ne viennent pas de ton constructeur.

    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); ?
    ...
    Bien vu, je vais corriger
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  2. #82
    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 Watilin Voir le message
    Attention cependant, dans les exemples que tu as montrés tu avais un constructeur qui faisait des choses aussi. Vérifie que ces duplications dont tu parles ne viennent pas de ton constructeur.
    Oui tout-à-fait tu as raison c'est pourquoi entre temps j'ai ajouté un EDIT...

    Citation Envoyé par Watilin Voir le message
    Ce n’est pas clair, il va falloir que tu réexpliques
    Oui je sais je devais te ré-expliquer ça, et j'avais dit que je le ferrais et je l'ai fait d'ailleurs mais je ne l'ai pas posté pour ne pas trop déranger ce fil...

    Mais peut-être que je pourrais "créer" une entrée blog" pour présenter la comparaisons des différentes méthodes, je n'ai encore jamais fait ça...

  3. #83
    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 réponds simplement au message #79, de Watilin, n'ayant pas le temps de bien lire ce qui suit.
    Citation Envoyé par Watilin Voir le message
    la propriété constructeur.prototype et l’accesseur-mutateur instance.__proto__ font tous les deux référence à cette propriété interne.
    J'avais hésité un peu à parler de la propriété __proto__, c'était surtout pour avoir une "propriété" à comparer avec prototype.

    Oui, en lecture, on a une référence similaire.
    Après en écriture, l'avertissement dit d'éviter par exemple de changer le prototype de "mavoiture", actuellement positionné sur le modèle "Car", pour aller le mettre sur le modèle "Maison".
    Ils disent qu'il est préférable de créer un objet "Maison".
    On peut imaginer que ce serait assez riche en implications sur l'instance "mavoiture" que de la "convertir" en maison.
    En revanche redéfinir la propriété Car.prototype paraît sans effet sur "Car".
    Bon je dis ça un peu au hasard, c'est à creuser en fait, il vaut mieux faire quelques tests et recherches complémentaires pour une idée plus précise.

    Après ça paraît un peu étrange d'être amené à changer le prototype d'un objet, mais par exemple, si je me souviens bien en AS1 c'était une pratique courante pour permettre d'associer un clip à une sous-classe.

    Sinon, je reste relativement sceptique vis à vis du côté un peu alarmiste de certaines mises en garde sur l'optimisation.
    Sans généraliser, ce n'est pas toujours si lourd que ça.
    Un facteur aggravant c'est si le code est exécuté sur le serveur car ça peut vite prendre de l'ampleur en fonction du nombre d'utilisateurs...

    C'est regrettable que l'avertissement ne soit pas un peu détaillé, un ordre de grandeur sur le coût et un mini-exemple n'auraient pas été de trop.

    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.
    Oui, mais c'est bien ce que j'avais en tête.
    Je voulais dire qu'on avait effectivement des propriétés qui pointent vers les mêmes fonctions, et donc que l'impact sur la mémoire est léger.

    • 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.
    Oui je sais aussi, c'est ce que je dis, mais malheureusement j'étais pressée de finir mon message dont le dernier paragraphe en particulier n'est effectivement pas des plus limpide.
    Mais ce que je dis aussi, c'est que je préfère le "premier cas".

    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.
    Tout à fait.

    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.
    Non bien sûr, ce n'est pas ce à quoi je pense.
    Je reconnais que ma phrase pouvait être comprise ainsi.

    Je clarifie avec un exemple :
    Ca, j'aime assez :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Car.prototype.fonction1=...
    Car.prototype.fonction2=...
    Car.prototype.nombre1=123
     
    Berline.prototype.fonction2=... //ici par exemple on redifinit fonction 2 (override)
    Berline.prototype.nombre2=... //un nombre qui ne concerne que les berlines
    Ca, j'aime moins :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Car.prototype.fonction1=...
    Car.prototype.fonction2=...
    Car.prototype.nombre1=123
     
    Berline.prototype.fonction1=Car.prototype.fonction1 //un peu inutile
    Berline.prototype.fonction2=... //ici par exemple on redifinit fonction 2 (override)
    Berline.prototype.nombre1=Car.prototype.nombre1 //un peu inutile
    Berline.prototype.nombre2=... //un nombre qui ne concerne que les berlines
    Le deuxième cas est surtout rendu nécessaire quand on veut faire de l'héritage multiple, je pense.

    Quant au message #78 de SylvainPV, oui j'avais bien compris, il n'y a pas de souci.

  4. #84
    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,

    Merci au fait pour vos réponses concernant vos préférences parmi les différents moyens de construire des objets...

    Justement concernant les fonctions usines, dans le cas où on veut construire plusieurs objets , je me demandais ce qui était le mieux entre :

    1- Exécuter n fois la fonction pour construire les n objets souhaités.
    2- Exécuter 1 fois la fonction pour construire un objet puis utiliser Object.create avec cet objet comme premier paramètre pour "construire" les n objets.

    Je crois que question mémoire*** la 2 est préférable mais y a-t-il autre chose à considérer pour faire le meilleur choix ?

    ---> Ou bien peut-être que les fonctions usines ne sont pas faites pour construire plusieurs objets ? Ce moyen-là ne serait pas judicieux dans ce cas-là ?

    *** Il semblerait que même les "variables privées" et les "méthodes privées" ne soient pas partagées (communes) et que chaque objet a ses propres copies.

    Voici un exemple pour clarifier ma question :

    Code javascript : 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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    var myFactory = function (name) {
     
        var uneVar = "une variable locale \"privée\"...";
     
        function uneFonction() {
            console.log("Exécution de la méthode \"privée\" uneFonction via log...");
        }
        console.log("\"construction\" de l'objet : ", name); // 
     
        return {
            // setter qui permet de changer la valeur de uneVar
            setUneVar: function setUneVar(valeur) {
                uneVar = valeur;
            },
     
            // getter qui permet de récupérer la valeur de uneVar
            getUneVar: function getUneVar() {
                return uneVar;
            },
     
            // Fonction Publique qui appelle une privée
            log: function log() {
                uneFonction();
            }
        };
    };
     
    // Méthode 1 : 
    var obj1 = myFactory("obj1");
    var obj2 = myFactory("obj2");
    var obj3 = myFactory("obj3");
     
    obj1.setUneVar("setUneVar obj1");
    obj2.setUneVar("setUneVar obj2");
    obj3.log(); // "Exécution de la méthode "privée" uneFonction via log..."
    console.log(obj1.getUneVar());
    console.log(obj2.getUneVar());
    console.log(obj3.getUneVar());
     
    // Méthod 2 : 
    var objC = myFactory("objC");
    var objC1 = Object.create(objC);
    var objC2 = Object.create(objC);
    var objC3 = Object.create(objC);
     
    objC1.setUneVar("setUneVar objC1");
    objC2.setUneVar("setUneVar objC2");
    objC3.log(); // "Exécution de la méthode "privée" uneFonction via log..."
    console.log(objC1.getUneVar());
    console.log(objC2.getUneVar());
    console.log(objC3.getUneVar());

    Merci.

  5. #85
    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
    Si tu utilises Object.create, c'est pour créer un objet à partir d'un prototype donc établir un lien d'héritage. On fait cela avec une intention très différente de celle qui est de simplement obtenir une copie de cet objet, ces liens d'héritage définissent la manière dont tu appréhendes ton modèle de données. Donc la question n'a pas vraiment de sens pour moi, c'est comme demander s'il vaut mieux avoir un fils ou un frère.
    One Web to rule them all

  6. #86
    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.

    Ben en fait les objets construits avec des fonctions usines peuvent avoir accès à un ensemble de variables et de méthodes, si j'ai besoin de plusieurs objets utilisant cet ensemble (de variables et de méthodes) eh bien je me disais qu'il serait dommage que chacun de ces objets ait chacun sa propre copie de cet ensemble (de variables et de méthodes) vue que c'est le même...

    En fait je pensais que par ce moyen on pouvait faire une sorte de librairie auquel aurait accès chaque objet... Il y aurait plusieurs objets mais une seule copie de la librairie...

    ---> Sinon j'ai du mal à voir quel est l’intérêt (par rapport au autre moyens de construire des objets) des fonctions usines si elles servent seulement à construire des objets plus ou moins identiques ?

  7. #87
    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
    On en revient à la discussion précédente, comment établir si une propriété est propre ou héritée. Toutes les voitures ont 4 roues et une méthode drive, ce sont donc des propriétés héritées, mais chaque voiture a son propre modèle et sa propre couleur, ce sont des propriétés propres. Le principe de la prog objet est de reproduire un concept, une entité associable au monde physique afin de se le représenter plus facilement. L'usage mémoire et les micro optimisations sont hors de propos. Tout ce qui compte c'est d'avoir une hiérarchie d'objets compréhensible, qui ait du sens.

    L'intérêt des factory functions, ben je pense que je n'ai pas besoin de t'expliquer l'intérêt des fonctions à la base. Maintenant, une fonction qui retourne un objet est soit invoquée en constructeur avec new, soit une factory function. Et j'ai ouvert ce topic précisément pour lister tous les problèmes que pose "new", problèmes qui ne se posent pas avec les factory functions. Que faut-il dire de plus ?
    One Web to rule them all

  8. #88
    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,
    Citation Envoyé par Beginner. Voir le message
    Il semblerait que même les "variables privées" et les "méthodes privées" ne soient pas partagées (communes) et que chaque objet a ses propres copies.
    Pour la seconde méthode, je constate au contraire que tout est en commun, on se réfère à "objC" et dans le cadre de l'unique appel à la fonction "myFactory".


    Je reviens sur la redéfinition de la propriété prototype d'une fonction et les diverses implications.
    Je ne parle pas ici de la modification du prototype d'une instance, opération indiquée comme coûteuse (et pas que directement) dans l'avertissement dont on a parlé.
    Je veux juste voir si la redéfinition de la propriété prototype d'une fonction est également coûteuse ou déconseillée.
    C'est une analyse personnelle uniquement, n'hésitez pas à relever des erreurs ou des oublis.


    Je commence avec la valeur par défaut de la propriété prototype d'une fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function Berline(){}
    console.log(Berline.prototype.constructor); //function Berline
    console.log(Berline.prototype instanceof Berline); //false
    On peut se demander comment il est possible que le constructeur de Berline.prototype soit la fonction "Berline" alors que Berline.prototype n'est pas une instance de "Berline".
    L'explication est simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    console.log(Berline.prototype.hasOwnProperty("constructor")); //true
    delete Berline.prototype.constructor;
    console.log(Berline.prototype.constructor); //function Object
    console.log(Berline.prototype.hasOwnProperty("constructor")); //false
    console.log(Object.getPrototypeOf(Berline.prototype)===Object.prototype); //true
    En effet, Berline.prototype.constructor est juste une propriété propre qui masque le constructeur réel de Berline.prototype, à savoir Object.
    Elle est automatiquement ajoutée afin de bien renseigner les instances de "Berline" sur leur constructeur.


    Maintenant si on redéfinit Berline.prototype :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function Car(){}
    function Berline(){}
    Berline.prototype=Object.create(Car.prototype);
    console.log(Berline.prototype.constructor); //Car
    console.log(Berline.prototype instanceof Car); //true
    console.log(Berline.prototype.hasOwnProperty("constructor")); //false
    Berline.prototype.constructor=Berline;
    console.log(Berline.prototype.hasOwnProperty("constructor")); //true
    On remarque qu'il n'y a plus de propriété propre constructor, mais on peut la recréer si besoin afin que les instances de "Berline" s'y réfèrent.


    Ensuite en redéfinissant Berline.prototype, quelle est l'implication sur les instances déjà créées ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function Car(){}
    function Berline(){}
    Berline.prototype.nombre=123;
    var prototypeInitialBerline=Berline.prototype;
    var b=new Berline();
    Berline.prototype=Object.create(Car.prototype);
    Berline.prototype.nombre=456;
    console.log(Object.getPrototypeOf(b)===prototypeInitialBerline); //true
    console.log(b.nombre); //123
    console.log(b instanceof Berline); //false, donc il vaut mieux éviter de redéfinir la propriété Berline.prototype si on veut s'y retrouver
    On observe deux choses :
    - Quand on redéfinit Berline.prototype, il n'y a aucune fonction appelée modifiant le prototype des instances déjà créées.
    Berline.prototype n'est après tout pas un accesseur.
    - Quand on accède à une propriété non propre d'une instance, la propriété n'est pas cherchée sur l'objet instance.constructor.prototype actuel.
    On se réfère uniquement au prototype à partir duquel est définie l'instance.


    En redéfinissant Berline.prototype, quelle est à présent l'implication sur les instances qui suivent ?
    Elle se situe au niveau de la propriété constructor :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function Car(){}
    function Berline(){}
    console.log(new Berline().constructor); //function Berline
    Berline.prototype=Object.create(Car.prototype);
    console.log(new Berline().constructor); //function Car
    Berline.prototype.constructor=Berline; //mais on peut le redéfinir
    console.log(new Berline().constructor); //function Berline

    Quelle est l'implication sur la classe "Berline" ?

    Si on considère que la fonction constructeur "Berline" et la propriété Berline.prototype définissent la classe "Berline", alors il est probable que ces deux éléments ne fonctionnent obligatoirement de pair que quand on utilise new.
    En dehors de cet opérateur, les deux peuvent être considérés séparément, on peut prendre un constructeur/une fonction d'initialisation à un endroit et le prototype à un autre (mais avec les conséquences que ça entraînera vis à vis de l'opérateur instanceof).
    Redéfinir Berline.prototype n'a d'incidence sur la classe "Berline" que si on considère "Berline" en tant que classe justement.
    On ne peut donc pas parler d'implication réelle sur un concept abstrait.


    Quelle est l'impact sur les performances ?

    De ce qui précède, je conclus que redéfinir Berline.prototype revient simplement à créer un objet.


    Deux remarques encore :

    - A noter que quand on utilise une méthode extend afin de modifier uniquement le contenu de la propriété Berline.prototype, la propriété constructeur reste positionnée sur "Berline" sans qu'on ait besoin de la redéfinir.
    - Il faut bien sûr éviter de redéfinir la propriété prototype après avoir créé des instances, à moins d'un besoin particulier. J'ai abordé la question uniquement pour évaluer les implications.


    Voilà donc une première analyse que vous pourrez rectifier ou compléter si besoin.
    Je pense et espère que ça reste assez bien dans le cadre du sujet initial.

  9. #89
    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 setUneVar, getUneVar, log uneVar et uneFonction
    Bonjour,

    Citation Envoyé par Loralina Voir le message
    Citation Envoyé par Beginner. Voir le message
    ***Il semblerait que même les "variables privées" et les "méthodes privées" ne soient pas partagées (communes) et que chaque objet a ses propres copies.
    Pour la seconde méthode, je constate au contraire que tout est en commun, on se réfère à "objC" et dans le cadre de l'unique appel à la fonction "myFactory".
    Oui bien sûr et c'était bien le but... Je n'ai peut-être pas été assez clair mais en fait le passage que vous citez c'était justement pour insister* sur le fait qu'avec la méthode 1 on consomme plus de mémoire qu'avec la méthode 2 c'est pourquoi j'écrivais juste avant :

    Citation Envoyé par Beginner. Voir le message
    Je crois que question mémoire*** la 2 est préférable mais y a-t-il autre chose à considérer pour faire le meilleur choix ?
    * Pour insister oui puisque ---> non seulement obj1, obj2 et obj3 ont leur propre copie des "méthodes publiques" setUneVar, getUneVar et log mais en plus ils ont même leur propre copie de uneVar ("variable privée") et de uneFonction ("méthode privée").


    ---> J’aurais dû être plus précis en disant : Il semblerait qu'avec la méthode 1 même les "variables privées" et les "méthodes privées" ne soient pas partagées (communes) et que chaque objet a ses propres copies

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

    Citation Envoyé par SylvainPV Voir le message
    L'usage mémoire et les micro optimisations sont hors de propos. Tout ce qui compte c'est d'avoir une hiérarchie d'objets compréhensible, qui ait du sens.
    Ben j'ai cru comprendre au contraire que ces optimisations comptent puisque qu'encore une fois : "on nous conseille 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...".

    Et là c'est pire car je parlais carrément d'une librairie, il est préférable qu'elle soit partagée par toutes les instances, non ?


    Citation Envoyé par SylvainPV Voir le message
    On en revient à la discussion précédente, comment établir si une propriété est propre ou héritée.
    Ben oui mais avec une variante quand même : la fonction usine. C'est pareil que dans ton premier message avec Object.create sauf que l'objet de départ est l'objet retournée par la fonction usine et non un objet littéral ou un objet construit comme tu l'as fait avec Object.create(Object.prototype);.

    Peut-être que pour être plus clair j'aurais dû reprendre ton exemple :

    Citation Envoyé par SylvainPV Voir le message
    ...
    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
    Avec la fonction usine :
    Code javascript : 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
    var myFactory = function () {
         return {
             wheels: 4,
             drive: function drive() {
                 this.speed = 120;
             }
         };
     };
     
     var Car = myFactory();
     
     var golf = Object.create(Car);
     golf.constructor = "Volkswagen";
     golf.model = "Golf";
     
    Object.getPrototypeOf(golf) === Car; // true ! ENFIN

    Bien sûr là, la fonction usine n’apporte rien mais dans d'autres cas, d'une manière générale, on peut, avec une fonction usine, passer des paramètres, ajouter des "variables privées" et/ou des "méthodes privées"...

    ---> Voilà ma question reformulée serait : si je veux construire plusieurs voitures, quel est le mieux :

    1- Exécuter n fois la fonction usine.
    2- Exécuter 1 fois la fonction pour construire un objet Car puis utiliser Object.create avec cet objet comme premier paramètre pour "construire" les n voitures.

    Bref, c'est juste pour clarifier que j'ai reformulé et non pour reposer ma question.

  10. #90
    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,
    J'avais en effet cru que les trois étoiles "***" se rapportaient à la deuxième méthode ("...*** la 2...").
    Malgré tout, je ne comprends pas cette comparaison entre les deux méthodes puisqu'elles ne donnent pas le même résultat (c'est ce que je voulais dire) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //méthode 1
    console.log(obj1.getUneVar()); //setUneVar obj1
    console.log(obj2.getUneVar()); //setUneVar obj2
    console.log(obj3.getUneVar()); //une variable locale "privée"...
     
    //méthode 2
    console.log(objC1.getUneVar()); //setUneVar objC2
    console.log(objC2.getUneVar()); //setUneVar objC2
    console.log(objC3.getUneVar()); //setUneVar objC2
    Ben j'ai cru comprendre au contraire que ces optimisations comptent
    Elles comptent, mais la marge est très grande.
    C'est plus pour la beauté du code (quoique si on pousse trop loin, ce ne sera pas forcément très joli) et/ou la satisfaction de se dire qu'on a fait quelque chose d'ultra optimisé.
    Concrètement, c'est surtout sur les applications très gourmandes, comme certains jeux, que ce genre de détails va jouer, la somme des optimisations finissant par faire une différence perceptible.

  11. #91
    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 Loralina Voir le message
    Malgré tout, je ne comprends pas cette comparaison entre les deux méthodes puisqu'elles ne donnent pas le même résultat (c'est ce que je voulais dire) :
    Ah ça c'était pour moi, car je n'étais pas sûr qu'avec la méthode 1 chaque objet avait sa propre copie de uneVar ("variable privée")...Les console.log montrent que c'est bien le cas apparemment (avec la méthode 1 mais pas avec la méthode 2 bien sûr).

  12. #92
    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 Beginner. Voir le message
    Ben j'ai cru comprendre au contraire que ces optimisations comptent puisque qu'encore une fois : "on nous conseille 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...".
    L'économie de mémoire est une conséquence d'un modèle bien pensé, pas l'inverse. Si tu en arrives au point d'avoir un problème de mémoire à cause d'une mauvaise utilisation des prototypes, c'est que ton modèle est mal conçu à la base. Pense ton modèle intelligemment et tu n'auras jamais le moindre problème de mémoire. Mais déformer un modèle pour faire de la micro optimisation prématurée, c'est le meilleur moyen de se planter.


    Citation Envoyé par Beginner. Voir le message
    Et là c'est pire car je parlais carrément d'une librairie, il est préférable qu'elle soit partagée par toutes les instances, non ?
    Je ne sais pas de quel genre de bibliothèque tu veux parler, mais j'imagine mal une bibliothèque qui viendrait s'incruster dans les propriétés de tes objets de données. Généralement, les libs encapsulent les données utilisateurs dans leurs propres objets ou utilisent des fonctions pures. Mais elles ne viennent certainement pas modifier tes propres prototypes, ce serait considéré comme une très mauvaise pratique, un peu comme ce que faisait Prototype ou Mootools il y a une dizaine d'années.


    Citation Envoyé par Beginner. Voir le message
    Ben oui mais avec une variante quand même : la fonction usine. C'est pareil que dans ton premier message avec Object.create sauf que l'objet de départ est l'objet retournée par la fonction usine et non un objet littéral ou un objet construit comme tu l'as fait avec Object.create(Object.prototype);.
    Je ne vois pas le rapport entre établir si une propriété doit être propre ou héritée, et utiliser une fonction ou pas pour instancier l'objet. En fait il n'y a aucun, mon code se comporte exactement de la même façon si tu remplaces myFactory par une déclaration littérale. J'ai l'impression que tu essaies de faire des liens là où il n'y en a pas.

    Citation Envoyé par Beginner. Voir le message
    Bref, c'est juste pour clarifier que j'ai reformulé et non pour reposer ma question.
    J'avais bien compris la première fois et je te réponds la même chose, tu nous demandes s'il vaut mieux avoir un frère ou un fils, il n'y a pas de réponse, la question n'a juste pas de sens.
    One Web to rule them all

  13. #93
    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
    Je profite que le flux de posts se soit un peu calmé sur ce topic pour revenir sur un des principaux problèmes qui m'avait bloqué dans ma démarche du bannissement de `new` et des constructeurs: le débogage et les noms de fonctions. Le fait de ne plus pouvoir se reposer sur le nom de la fonction constructeur est une régression sur l'expérience de développement qu'il me fallait remédier. C'est chose faite à présent que j'ai découvert les customDevtoolsFormatters. Attention toutefois, ce n'est pas un standard et seul Chrome les supporte actuellement, mais des tickets sont ouverts côté Firefox et Edge pour les implémenter.

    Voici un tuto sur ces formatteurs : http://www.mattzeunert.com/2016/02/1...ormatters.html

    Et voici comment j'ai procédé pour ajouter des noms à mes protos objets, sans constructeurs. Tout d'abord le code du formatteur:

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    const styles = {        
            list: `list-style-type: none; padding: 0; margin: 0;`,
            listItem: `padding: 0 0 0 1em;`,
            name: `color: #43a047; font-style: italic`,
            proto: `color: #4347a0; font-style: italic`,
            function: `color: #4271ae`,
            string: `color: #C41A16`,
            number: `color: #1C00CF`,
            boolean: `color: #AA0D91`,
            property: `color: #881391`,
            null: `color: #808080`
        },
     
     
        span  = (value, style) => ["span", {style}, value],
     
     
        format = (x, config) => {
            if (x === null || x === undefined) return span(String(x), styles.null);
            if (typeof x === "boolean") return span(x, styles.boolean);
            if (typeof x === "number") return span(x, styles.number);
            if (typeof x === "string") return span(`"${x}"`, styles.string);
            if (typeof x === "function") return span(x.name || x.toString(), styles.function);
            return x ? ['object', {object: x, config}] : null
        },
     
     
        formatBody = (o, config) => {
            return [ 'ol', {style: styles.list}, '{', ...Object.keys(o).map(prop => {
                    return ['li', {style: styles.listItem}, span(prop, styles.property), ': ', format(o[prop], config)]
                }),
                ['li', {style: styles.listItem}, span("[Prototype]", styles.proto), ': ', format(Object.getPrototypeOf(o), config)],
            '}'];
        }
     
     
    const Name = Symbol("Name");
    if (typeof window !== "undefined") {
        const NamedObjectFormatter = {
            header(o) {
                if (o && o[Name]) return span(o[Name], styles.name)
            },
            hasBody(o) { return o instanceof Object },
            body(o) { return formatBody(o, {expanded: true}) }
        };
        window.devtoolsFormatters = (window.devtoolsFormatters || []).concat(NamedObjectFormatter);
    }
    Remarquez le Symbol Name ; c'es comme ça que j'ai résolu le problème du nom, en ajoutant à mes protos objets un Symbol contenant le nom du prototype afin de remplacer le nom de la fonction du constructeur. Grâce au Symbol, aucun risque de collision et il ne vient pas non plus polluer les propriétés de mes objets lorsque je veux les parcourir. C'est exactement ce qu'il nous faut !

    Et voilà ce que ça donne à l'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
    const Car = {
        [Name]: "Car",
        wheels: 4,
        speed: 0,
        drive(){
            this.speed = 120;
        },
        stop(){
            this.speed = 0;
        }
    };
     
     
    const New = proto => props => Object.assign(Object.create(proto), props) // ma version factory function de l'opérateur new
     
     
    // on crée un sous-prototype à partir du prototype Car
    const Yaris = New(Car)({
        [Name]: "Yaris",
        constructor: "Toyota",
        model: "Yaris"
    });
     
     
    const maVoiture = New(Yaris)({ owner: "Sylvain" })
    Après avoir activé les custom devtools formatters dans Chrome, voilà ce que ça donne comme expérience de débogage:
    Nom : llb9E5r.png
Affichages : 152
Taille : 40,8 Ko
    One Web to rule them all

  14. #94
    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


    @SylvainPV : comme tu le sais déjà, je n'ai jamais été à l'aise avec les "trucs et astuces" qui sortent "des clous". Mais je me suis laissé aller à un petit tripotage.

    C'est juste pour le "fun", je ne compte pas l'utiliser en production, mais je voudrais avoir ton opinion sur la validité et la solidité de la chose.

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    const 
        kCreateCar = ( {
            roues = 4,
            vitesse = 0,
        } = {} ) => ( { 
            nom : 'Car',
            roues,
            vitesse,
            conduire(){ this.vitesse = 120 },
            stop(){ this.vitesse = 0 },
        } ),
        kCreateYaris = ( {
            roues = 4,
            vitesse = 0,
            conducteur = 'Sylvain',
        } = {} ) => ( Object.assign( {}, kCreateCar( { roues, vitesse } ), {
            conducteur,
            constructeur : 'Toyota',
            modèle : 'Yaris',
        } ) );
     
    let oSylvainCar = kCreateYaris( { vitesse : 30 } );
     
    console.log( oSylvainCar.nom, oSylvainCar.vitesse );
     
    oSylvainCar.conduire();
     
    console.log( oSylvainCar.nom, oSylvainCar.vitesse );
    console.log( Object.getPrototypeOf( oSylvainCar ) );
    console.log(  kGetType( oSylvainCar ) );
    console.log( kLogVars`${ oSylvainCar }` );
     
    /*
    Car 30
    Car 120
    {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ,*…}constructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()
    object
    nom = Car, 
    roues = 4, 
    vitesse = 120, 
    conduire = conduire(){ this.vitesse = 120 }, 
    stop = stop(){ this.vitesse = 0 }, 
    conducteur = Sylvain, 
    constructeur = Toyota, 
    modèle = Yaris
    */

    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. #95
    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
    Se servir des valeurs par défaut des paramètres en lieu et place de propriétés héritées du prototype parent, c'est astucieux mais ce n'est pas tout à fait la même finalité. Les valeurs de paramètres par défaut et les prototypes sont deux fonctionnalités du langage qui sont utilisées conjointement et ce depuis très longtemps. Pas forcément via la syntaxe ES6, mais c'est juste du sucre syntaxique pour remplacer un Object.assign ou un $.extend, ou toute autre méthode existante pour gérer les options par défaut. Parmi les petites choses en plus qu'apportent les protos, j'ai en tête :
    - le fait que les propriétés héritées et leurs valeurs soient toutes répertoriées dans un objet accessible depuis un scope extérieur à celui de la fonction usine
    - le fait que les propriétés héritées peuvent être récupérées et utilisées dans leur version "surchargée", via le mot-clé super ou leConstructeur.prototype ou encore Object.getPrototypeOf() etc.
    - le fait que lorsqu'une propriété propre est supprimée via l'opérateur delete, sa valeur reprend celle de la propriété héritée dans le prototype parent le plus proche ayant cette propriété définie.

    Il y a donc une différence d'intention: l'héritage prototypal vient étendre/surcharger un existant, tandis que les valeurs par défaut viennent combler un manque/une absence.
    One Web to rule them all

  16. #96
    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
    A titre informatif, j'ai publié un article en anglais sur Medium qui reprend la plupart des points de cette actu, avec des détails et codes remis au goût du jour : https://medium.com/@SylvainPV/new-is...y-898b67ba7773
    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