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 :

Factories et opérateur instanceof en ES5


Sujet :

JavaScript

  1. #1
    Rédacteur/Modérateur

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

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut Factories et opérateur instanceof en ES5
    Hello,

    A mon tour de poser une question pour changer. Je souhaiterai une écrire une factory (fonction constructeur de fonctions) et respecter la chaîne prototypale, c'est-à-dire new Factory() instanceof FactoryAvec les constructeurs classiques retournant un objet, c'est le comportement par défaut. Mais quand il s'agit de retourner une fonction, c'est beaucoup plus délicat.

    Exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function MultiplierFactory(n1){
        return function(n2){ return n1*n2; };
    }
     
    var multiplyBy3 = new MultiplierFactory(3);
    multiplyBy3(7) 
    > 21
    multiplyBy3 instanceof MultiplierFactory
    > false
    La seule option que j'ai trouvé est de modifier dynamiquement le prototype via Object.setPrototypeOf:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function MultiplierFactory(n1){
        var f = function(n2){ return n1*n2; };
        Object.setPrototypeOf(f, MultiplierFactory.prototype);
        return f;
    }
     
    var multiplyBy3 = new MultiplierFactory(3);
    multiplyBy3(7) 
    > 21
    multiplyBy3 instanceof MultiplierFactory
    > true
    Ce qui fonctionne sur tous les navigateurs supportant Object.setPrototypeOf. Mais que faire dans le cas d'IE9 et inférieur ? Existe-t-il une solution en EcmaScript 5 pour parvenir à ce résultat ?

    Merci d'avance
    One Web to rule them all

  2. #2
    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
    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
    // "Class" Multiplier
    function Multiplier( n1, n2 ){
        this.result = n1 * n2;
    }
     
    // Construction de la factory avec n1 = 7
    // pattern : http://addyosmani.com/resources/essentialjsdesignpatterns/book/#factorypatternjavascript
     
    function MultiplierFactory(){}
     
    MultiplierFactory.prototype.multiplierClass = Multiplier;
     
    MultiplierFactory.prototype.createMultiplier = function( n2 ){
        return new this.multiplierClass( 7, n2 );
    };
     
    // Utilisation de la factory
     
    var multiplierFactory = new MultiplierFactory(),
        multiplierBy3 = multiplierFactory.createMultiplier( 3 ),
        multiplierBy4 = multiplierFactory.createMultiplier( 4 ),
        multiplierBy5 = multiplierFactory.createMultiplier( 5 );
     
    // true
    console.log( multiplierBy3 instanceof Multiplier );
     
    // 7 * 3 = 21
    console.log( multiplierBy3.result );
     
    // true
    console.log( multiplierBy4 instanceof Multiplier );
     
    // 7 * 4 = 28
    console.log( multiplierBy4.result );
     
    // true
    console.log( multiplierBy5 instanceof Multiplier );
     
    // 7 * 5 = 35
    console.log( multiplierBy5.result );
    EDIT : je réalise que je n'ai pas répondu au problème : "retourner une fonction". Mais que l'on passe par le pattern ou par Object.setPrototypeOf() on en arrive toujours à manipuler un objet.

    EDIT2 :

    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
    Object.setPrototypeOf = Object.setPrototypeOf || function( obj, proto ){
      obj.__proto__ = proto;
      return obj; 
    };
     
    // test obj.__proto__ = proto;
     
    function MultiplierFactory(n1){
        var f = function(n2){ return n1*n2; };
        f.__proto__ = MultiplierFactory.prototype;
        return f;
    }
     
    var multiplyBy3 = new MultiplierFactory(3);
     
    console.log( multiplyBy3(7) );
    console.log( multiplyBy3 instanceof MultiplierFactory );

    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.)

  3. #3
    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
    Solution avec Object.create() :

    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
    function MultiplierFactory( n1 ){
        return function( n2 ){
            return n1*n2;
        };
    }
     
    MultiplierFactory.prototype = {};
     
    var multiplyBy3 = Object.create( MultiplierFactory.prototype, {
        "f" : {
            "value" : MultiplierFactory( 7 )
        }
    });
     
    console.log( multiplyBy3.f( 3 ) );
    console.log( multiplyBy3 instanceof MultiplierFactory );
    console.log( Object.getPrototypeOf( multiplyBy3 ) );

    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.)

  4. #4
    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
    __proto__ n'est pas standard et n'est pas présent non plus sur IE <= 9.

    Les autres solutions proposées ne retournent pas une fonction. J'ai besoin que multiplyBy3 instanceof MultiplierFactory && multiplyBy3 instanceof Function. C'est là le coeur du problème
    One Web to rule them all

  5. #5
    Membre chevronné

    Homme Profil pro
    Ingénieur Hospitalier
    Inscrit en
    Juillet 2004
    Messages
    993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Hospitalier
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 993
    Points : 1 768
    Points
    1 768
    Billets dans le blog
    1
    Par défaut Pas top :(
    Salut sujet intéressant j'ai trouvé une solution mais je sais pas si cela est ce que tu recherche vraiment :

    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
     
    function MultiplierFactory(n1){
        var f = function(n2){ return n1*n2; };
        //f.__proto__ = MultiplierFactory.prototype   //ES6
        //Object.setPrototypeOf(f, MultiplierFactory.prototype); //ES6
       //TEST ES5 -> jusqu'a IE5 en émulation
        f.constructor.prototype = function(f) {
           function f () {}
           f.prototype = proto
           return new f
        }
     
        return f;
    }
     
     
    var multiplyBy3 = new MultiplierFactory(3);
    console.log(multiplyBy3(7))
     
    multiplyBy3 instanceof MultiplierFactory
     
    MultiplierFactory.prototype = new MultiplierFactory(7);
     
    var multiplyBy4 = new MultiplierFactory(7);
    console.log(multiplyBy4(7))
    multiplyBy4 instanceof MultiplierFactory.constructor; // true
    Je sais pas si c'est ce que tu souhaite j'aurais testé au moins gl.

  6. #6
    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
    Citation Envoyé par SylvainPV Voir le message
    J'ai besoin que multiplyBy3 instanceof MultiplierFactory && multiplyBy3 instanceof Function. C'est là le coeur du problème
    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
    function getType( Obj ){
        return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
    }
     
    function MultiplierFactory(n1){
        var f = function( n2 ){ return n1*n2; };
        Object.setPrototypeOf( f, MultiplierFactory.prototype );
        return f;
    }
     
    MultiplierFactory.prototype = Function.prototype;
     
    var multiplyBy3 = new MultiplierFactory( 7 );
     
    console.log( multiplyBy3( 3 ) ); // 21
    console.log( getType( multiplyBy3 ) ); // function
    console.log( multiplyBy3 instanceof MultiplierFactory ); // true
    console.log( multiplyBy3 instanceof Function ); // true
    Citation Envoyé par SylvainPV Voir le message
    __proto__ n'est pas standard et n'est pas présent non plus sur IE <= 9.
    Oui. Comme d'habitude, le vrai problème c'est les versions obsolètes de IE.

    Citation Envoyé par SylvainPV Voir le message
    Les autres solutions proposées ne retournent pas une fonction.
    Oui. Avec Object.create() il sera difficile de faire autrement.

    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
    function getType( Obj ){
        return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
    }
     
    function MultiplierFactory( n1 ){
        return function( n2 ){
            return n1*n2;
        };
    }
     
    MultiplierFactory.prototype = Function.prototype;
     
    var multiplyBy3 = Object.create( MultiplierFactory.prototype, {
        "f" : {
            "value" : MultiplierFactory( 7 )
        }
    });
     
    console.log( multiplyBy3.f( 3 ) ); // 21
    console.log( getType( multiplyBy3.f ) ); // function
    console.log( multiplyBy3 instanceof MultiplierFactory ); // true
    console.log( multiplyBy3 instanceof Function ); // true
    Citation Envoyé par SylvainPV Voir le message
    J'ai besoin que multiplyBy3 instanceof MultiplierFactory && multiplyBy3 instanceof Function. C'est là le coeur du problème
    C'est aussi la solution.

    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
    function getType( Obj ){
        return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
    }
     
    function MultiplierFactory( n1 ){
        return function( n2 ){
            return n1*n2;
        };
    }
     
    MultiplierFactory.prototype = Function.prototype;
     
    var multiplyBy3 = new MultiplierFactory( 7 );
     
    console.log( multiplyBy3(3) ); // 21
    console.log( getType( multiplyBy3 ) ); // function
    console.log( multiplyBy3 instanceof MultiplierFactory ); // true
    console.log( multiplyBy3 instanceof Function ); // true

    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.)

  7. #7
    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
    @headmax: non ça ne va pas non plus, avec la redéfinition du prototype multiplyBy4 n'est pas une instance de MultiplierFactory. MultiplierFactory.constructor correspond simplement au constructeur Function.

    @daniel: oui je crois que tu as trouvé avec ton dernier exemple Mais je ne parviens pas à comprendre comment ça marche... comparé à mon exemple initial, il a fallu simplement rajouter MultiplierFactory.prototype = Function.prototype; pour valider le test d'instanceof sur MultiplierFactory (et non celui sur Function ??? j'aurais cru l'inverse).

    J'ai aussi une autre contrainte que je n'ai pas encore mentionné, c'est que j'ai des variables et méthodes dans le prototype de mes factories. S'il correspond à Function.prototype, je vais modifier le prototype de toutes les fonctions et ce n'est pas du tout ce que je souhaite. Or je ne vois pas comment hériter de ce prototype sans casser la solution...

    edit: je pense avoir compris. En assignant le proto de la Factory au proto de Function, on aura toujours model instanceof Factory && model instanceof Function puisque Object.getPrototypeOf(model) === Function.prototype && Function.prototype === Factory.prototypeLa Factory produit des fonctions avec le prototype de base des fonctions, donc toute fonction déclarée (dont celle de l'instruction return dans le code de la Factory) sera assimilée à une instance de cette Factory. Si je déclare n'importe quelle fonction, on aura:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    console.log( (function(){}) instanceof MultiplierFactory );
    > true
    Donc ça marche, mais ça ne résout pas du tout mon problème puisque ça revient à déclarer la fonction normalement. Or mon but était de produire des fonctions spécialisées, des fonctions avec un certain prototype particulier héritant de Function.prototype.
    One Web to rule them all

  8. #8
    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
    Créer un objet (new) qui retourne une fonction (qui doit être une instance de MultiplierFactory et de Function). Cette fonction doit aussi se comporter comme un objet (une Function est un objet).

    C'est "space" comme disent les Bruxellois, "oufti" comme disent les Liègois.

    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
    function getType( Obj ){
        return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
    }
     
    function MultiplierFactory( n1 ){
        return function( n2 ){
            return n1*n2;
        };
    }
     
    MultiplierFactory.prototype = Function.prototype;
     
    Object.defineProperties( MultiplierFactory.prototype, {
        "n" : {
            "value" : 150,
            "enumerable" : true,
            "writable" : true
        },
        "toString" : {
            "value" : function( value ){
                var str = value || "toString :";
     
                str += " this.n = " + this.n;
     
                return str;
            },
            "enumerable" : true,
            "writable" : true
        }
    });
     
    var multiplyBy3 = new MultiplierFactory( 7 );
     
    console.log( multiplyBy3(3) ); // 21
    console.log( getType( multiplyBy3 ) ); // function
    console.log( multiplyBy3 instanceof MultiplierFactory ); // true
    console.log( multiplyBy3 instanceof Function ); // true
     
    console.log( multiplyBy3.n ); // 150
    console.log( multiplyBy3.toString() ); // toString : this.n = 150

    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.)

  9. #9
    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
    defineProperties ne change pas grand chose au schmilblick...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    console.log( (function(){}).n );
    > 150
    Mon objectif est si "space" que ça ? Une fonction étant un objet en JS, on devrait pouvoir en créer avec des prototypes personnalisés comme n'importe quel autre objet. Mais j'ai l'impression que l'opérateur new montre une fois de plus ses limites...
    One Web to rule them all

  10. #10
    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
    Hé oui !

    Tu dois avoir des raisons que j'ignore pour te compliquer la vie, car il est beaucoup plus simple de travailler avec une construction classique.

    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
    function getType( Obj ){
        return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
    }
     
    function MultiplierFactory( n1 ){
        this.n1 = n1;
    }
     
    MultiplierFactory.prototype = Object.create( Object.prototype, {
        "f" : {
            "value" : function( n2 ){
                return this.n1 * n2;
            },
            "enumerable" : true
        },
        "n" : {
            "value" : 150,
            "enumerable" : true,
            "writable" : true
        },
        "toString" : {
            "value" : function( value ){
                var str = value || "toString :";
     
                str += " this.n = " + this.n;
     
                return str;
            },
            "enumerable" : true
        }
    });
     
    var multiplyBy3 = new MultiplierFactory( 7 );
     
    console.log( multiplyBy3.f(3) ); // 21
    console.log( getType( multiplyBy3.f ) ); // function
    console.log( multiplyBy3.f instanceof Function ); // true
     
    console.log( multiplyBy3 instanceof MultiplierFactory ); // true
    console.log( multiplyBy3.n ); // 150
    console.log( multiplyBy3.toString() ); // toString : this.n = 150

    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.)

  11. #11
    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
    Pas de solution connue pour IE<=9, le mieux qu'on puisse faire c'est ça :

    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
    function MyFun() {
      if (!this || this==window) {
        return new MyFun();
      }
     
     
      var f = function() {
        return "thanks for calling!";
      }
     
     
      f.constructor = MyFun;
     
     
      if({__proto__:[]} instanceof Array){
        f.__proto__ = MyFun.prototype;
      }
     
     
      if(Object.setPrototypeOf){
        Object.setPrototypeOf(f, MyFun.prototype);
      }
     
     
      return f;
    }
     
     
    MyFun.prototype = {
      foo: function() {
        return "foo:" + this();
      },
      __proto__: Function.prototype
    };
     
     
    var f = new MyFun();
    console.log("proto method:"+f.foo()); // try our prototype methods
    console.log("function method:"+f.call()); // try standard function methods
    console.log("function call:"+f()); // try use as a function
    console.log('typeof:' + typeof f); // "function", not "object". No way around it in current js versions
    console.log('is MyFun:' + (f instanceof MyFun)); // true
    console.log('is Function:' + (f instanceof Function)); // true
    C'est clairement une erreur de conception du langage selon moi, à rajouter à la liste des problèmes que pose l'opérateur new.

    Pour le moment j'ai déprécié instanceof sur IE<=9 et je propose ma propre méthode pour tester si une fonction est une instance d'une factory donnée. On a donc un sham sur Object.setPrototypeOf et un sham sur instanceof:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var canSetProto = !!Object.setPrototypeOf || {__proto__:[]} instanceof Array;
    Object.setPrototypeOf = Object.setPrototypeOf || canSetProto
        ? function(o, p){ o.__proto__ = p; }
        : function(o, p){ for(var k in p){ o[k] = p[k]; } };
    function instanceofsham(obj, Constructor){
        return canSetProto ? obj instanceof Constructor : obj && (function recursive(o,c,p){
            return o && (c=o.constructor) && (p=c.prototype) && (p === Constructor.prototype || (o !== p && recursive(p)));
        })(obj)
    }
    J'espérais mieux mais bon, ça fait le job. Si quelqu'un connaît une autre option en EcmaScript 5 pour définir des fonctions au prototype personnalisé, ça m'intéresse grandement.
    One Web to rule them all

  12. #12
    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 des gens comme Douglos Crockford ou Eric Elliots recommandent depuis des années dans leurs bouquins et à leurs conférences d'arrêter d'utiliser new, je pense qu'on peut légitimement dire qu'il n'est pas exempt de problèmes. Malheureusement vu l'API que je dois exposer et le support navigateur que je dois respecter, je n'ai pas vraiment d'autre choix que de l'utiliser.

    Peu importe, ce n'est pas new le problème ici, d'ailleurs Object.create n'est d'aucun secours non plus. Il manquait quelque-chose au langage pour pouvoir profiter partout des prototypes, et Object.setPrototypeOf a sans doute été ajouté pour ça.
    One Web to rule them all

  13. #13
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 094
    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 094
    Points : 6 755
    Points
    6 755
    Par défaut
    Je ne vais pas répondre à l'aspect « IE9 » de la question mais je fais part de mes récentes découvertes :


    L'idée derrière la méthode magique __invoke c'est de nous donner le pouvoir (à nous, développeurs) d'appeler n'importe quel objet comme si c'était une fonction. C'est quelque chose qui n'existe pas encore en JavaScript, à ma connaissance.

    En revanche on a les symbols qui sont apparus avec ES6 et qui introduisent un moyen de modifier le comportement « interne » des objets, par exemple @@iterator nous permet de décrire le comportement qu'aura l'objet quand il sera utilisé dans une boucle for..of. Ça me fait penser à la surchage d'opérateurs en C++ : puissant, mais avec un gros potentiel bordélisant

    Vous voyez où je veux en venir : un nouveau symbol serait un candidat tout trouvé pour mettre en œuvre __invoke dans JavaScript. Mettons qu'il s'appellerait @@invoke. Ce nouveau venu nous donnerait la possibilité de créer un objet comme on l'entend tout en ayant la capacité de l'appeler comme une fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function MultiplierFactory(n1) {
      this.n1 = n1;
    }
     
    MultiplierFactory.prototype[Symbol.invoke] = function(n2) {
      return this.n1 * n2;
    };
    Le test instanceof réussit évidemment puisqu'on utilise la construction habituelle avec new.
    Bémol, j'ai été obligé d'attacher n1 comme propriété de l'objet. La méthode étant en dehors du constructeur, je ne pouvais pas bénéficier de la closure. On peut évidemment modifier sa visibilité en jouant avec defineProperty, mais la propriété est là, et ça peut faire tache. Une autre solution serait d'attacher la méthode dans le constructeur, mais on perd l'efficacité du prototype en faisant ça.

    Bref, je pense qu'on pourrait faire des trucs très intéressants si __invoke était implémenté en JS


    PS : je viens de voir qu'un symbol @@hasInstance est prévu mais pas encore implémenté, il permettrait de redéfinir le comportement de instanceof. Intéressant aussi !
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  14. #14
    Rédacteur/Modérateur

    Avatar de SpaceFrog
    Homme Profil pro
    Développeur Web Php Mysql Html Javascript CSS Apache - Intégrateur - Bidouilleur SharePoint
    Inscrit en
    Mars 2002
    Messages
    39 640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 74
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur Web Php Mysql Html Javascript CSS Apache - Intégrateur - Bidouilleur SharePoint
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2002
    Messages : 39 640
    Points : 66 669
    Points
    66 669
    Billets dans le blog
    1
    Par défaut
    C'est "space"
    Je n'y suis pour rien hein ...
    Ma page Developpez - Mon Blog Developpez
    Président du CCMPTP (Comité Contre le Mot "Problème" dans les Titres de Posts)
    Deux règles du succès: 1) Ne communiquez jamais à quelqu'un tout votre savoir...
    Votre post est résolu ? Alors n'oubliez pas le Tag

    Venez sur le Chat de Développez !

  15. #15
    Membre averti
    Profil pro
    à la bougie alors
    Inscrit en
    Mai 2006
    Messages
    224
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : à la bougie alors

    Informations forums :
    Inscription : Mai 2006
    Messages : 224
    Points : 362
    Points
    362
    Par défaut
    Citation Envoyé par SylvainPV Voir le message
    ce n'est pas new le problème ici, d'ailleurs Object.create n'est d'aucun secours non plus.
    alors pourquoi
    Citation Envoyé par SylvainPV Voir le message
    C'est clairement une erreur de conception du langage selon moi, à rajouter à la liste des problèmes que pose l'opérateur new.
    ?

    Il y a confusion entre héritage et type, ou je ne comprends pas. On peut trés bien hériter de Function.prototype ou String.prototype mais nous n'avons pas les outils, des constructeurs, qui permettraient de créer les types à partir d'un objet existant.
    C'est donc un problème de constructeurs plutot que de new.

    Comme les fonctions sont des objets, l'opérateur new se comporte normalement en créant le type en haut du graphe d'héritage. Ce qui manque c'est comment transformer un Object en Function (je parle des types : [[Class]]).
    Le même problème se pose avec String, Array, etc. On peut en hériter mais pas, hors des constructeurs définis, créer des objets de ces types.

    La seule solution restante est celle que tu as trouvée, c'est à dire de modifier le prototype d'un objet créé avec un des constructeurs 'built-in'.

    Dans ton cas, et dans pas mal d'exemples de code dans cette discussion, l'opérateur new est mal utilisé. La plupart du temps cela ne sert à rien, mis à part à conserver artificiellement une sémantique.

    Quand un constructeur retourne une valeur qui est un objet, l'objet créé par new est mis à la poubelle et donc la mise à jour du lien d'héritage avec. Autrement dit, c'est une erreur que de l'utiliser dans ce contexte.

    L'exemple que tu donnes en provenance de stackoverflow en est un bel exemple.

    A mon avis, dans ce cas, le pattern 'create' que tu avais proposé me semble plus adapté et moins sujet à "foutre le bordel dans la tête". Parce que visiblement ...

  16. #16
    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
    à rajouter en parallèle de la liste des problèmes de new aurais-je dû dire. Comme tu l'as fait remarquer, new se contente de faire ce pourquoi il a été conçu, or ici on bute sur ce qu'il ne fait pas. Après, est-ce que c'est cet opérateur qui est mal conçu ou est-ce qu'il aurait fallu partir sur une autre syntaxe, ça je laisse les grosses têtes de TC39 en décider.

    Je vais faire quelques essais sur des patterns différents, mais je ne peux pas vraiment faire ce que je veux sur ce projet. Tu as vu juste sur le fait de "conserver artificiellement une sémantique", c'est exactement ce dont il s'agit.
    One Web to rule them all

Discussions similaires

  1. [Binaire] Opérateurs de rotation dee bits ?
    Par Tifauv' dans le forum C
    Réponses: 3
    Dernier message: 09/11/2017, 11h29
  2. [Windev 9] équivalent de l'opérateur instanceof
    Par le y@m's dans le forum WinDev
    Réponses: 7
    Dernier message: 28/08/2007, 12h43
  3. Opérateur like
    Par Troopers dans le forum ASP
    Réponses: 3
    Dernier message: 15/09/2003, 19h19
  4. opérateur non applicable à ce type d'opérande
    Par Amon dans le forum Langage
    Réponses: 3
    Dernier message: 11/06/2003, 18h07
  5. [imprecis]Réaliser a^n avec seulement l'opérateur d'addition
    Par Amon dans le forum Algorithmes et structures de données
    Réponses: 18
    Dernier message: 08/11/2002, 22h22

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