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 :

Utilisation des getter/setter ES5 pour des tests null-safe et de la validation de type à la volée


Sujet :

JavaScript

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  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
    Par défaut Utilisation des getter/setter ES5 pour des tests null-safe et de la validation de type à la volée
    edit: J'ai continué à travailler sur ces expérimentations pour produire une bibliothèque grand public.
    Après un peu moins de deux mois de taf, voilà enfin le résultat : http://syllab.fr/projets/web/ObjectModel/

    Bonsoir les codeurs,

    J'étais en train de bricoler avec les getters/setters et Object.defineProperty pour voir si je ne pouvais pas m'en servir pour simuler l'API Proxy actuellement à l'étude en ES6 (voir https://developer.mozilla.org/fr/doc..._globaux/Proxy), et je suis arrivé à un truc très sympa.

    A la base, je cherchais à éviter ce genre de tests redondant dans mes codes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if(person != null && person.address != null && person.address.work != null && person.address.work.city != null){
        fonctionAvec(person.address.work.city);
    }
    A supposer que l'adresse est un champ faculatatif pour mon utilisateur, dès que je souhaite faire une opération avec un sous-champ bien précis, je dois tester toute la chaîne par prudence pour éviter les exceptions de type Cannot read property 'work' of undefined.

    Les Proxies pourraient permettre de simplifier ce code, en fournissant un objet à la place des valeurs null/undefined si on essaie de faire un getter dessus. Mais leur support est minoritaire et ils ne sont même pas confirmés par le W3C. Il y a aussi les monades pour ça, notamment Maybe, mais cela nous oblige à adapter notre syntaxe un peu partout. Je n'accroche pas beaucoup aux monades, elles font partie de ces choses très simples mais très difficiles à expliquer. Et puis elles ont un nom vraiment moche

    Donc en remplacement des Proxies, on a Object.defineProperty dont on pourrait se servir sur un objet . Le problème est que pour s'en servir, il faut savoir quelle clé définir ! C'est là que j'ai eu l'idée de fournir un "modèle" de données pour les objets que je veux tester. C'est vrai, on peut déclarer quelque-part que le modèle Person est amené à contenir des addresses, dont une professionnelle, avec une ville définie. Même si ces champs sont facultatifs, on les a en tête et on code des instructions avec, alors autant les déclarer quelque-part.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var Person = ObjectModel({ 
      name: String,
      age: Number,
      female: Boolean,
      address: {
        work: {
          city: String
        }
      }
    });
    Tout ce qu'il me fallait, c'était la liste des clés pour pouvoir les déclarer avec defineProperty sur mon objet proxy. Je n'avais pas besoin des valeurs, mais comme je ne savais pas quoi mettre, j'ai mis les types attendus. Et c'est là que m'est venu la seconde idée: faire de la validation de type à la volée. Bon, les erreurs seront remontées au runtime, ce n'est non plus du TypeScript, mais ça peut aider à prévenir certains problèmes de faire valider les types.

    Bref, en quelques heures j'ai réussi à pondre une petite fonction qui fait des miracles :

    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
    48
    49
    50
    51
    function ObjectModel(def) {
     
      function wrap(obj) {
        if (obj === null || obj === undefined) {
          return Object.create(null);
        } else switch (typeof obj) {
          case "string":
            return new String(obj);
          case "number":
            return new Number(obj);
          case "boolean":
            return new Boolean(obj);
          case "object":
          default:
            return obj;
        }
      }
     
      function isPrimitive(def){
        return def === String || def === Number || def === Boolean;
      }
     
      function safeProxy(obj, def) {
        var wrapper = wrap(obj);
        var proxy = Object.create(Object.getPrototypeOf(wrapper));
     
        if(isPrimitive(def)){
          if (obj !== undefined && def(obj) !== obj) {
            throw new Error("expecting " + def.name + ", got " + (typeof obj));
          }
          return obj;
        } else {
          Object.keys(def).forEach(function (key) {
            Object.defineProperty(proxy, key, {
              get: function () {
                return safeProxy(isPrimitive(def[key]) && obj instanceof Object ? obj[key] : wrapper[key], def[key]);
              },
              set: function (val) {
                wrapper[key] = safeProxy(val, def[key]);
              }
            });
          });
        }
     
        return proxy;
      }
     
      return function (obj) {
        return safeProxy(obj, def);
      };
    }
    Cette fonction permet donc de manipuler des objets au rôle de proxy qui vont être mis en parallèle avec un modèle à chaque get/set. Ce qui permet de faire ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    var joe = Person();
    joe.name = "Joe";
    joe.age = "old"; // Error: expecting Number, got string
     
    if(joe.address.work.city){ //joe.address n'est pas défini mais ce code ne lève pas d'exceptions
      //mais cette condition ne passe pas, car joe.address.work.city renvoie undefined
      throw new Error("you should not pass");
    }
    On peut aussi utiliser une syntaxe avec new :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    var ann = new Person;
    ann.female = 1; //Error: expecting Boolean, got number
    ann.age = 25;
    ann.name = joe.name+"'s wife";
    Je cherche à vérifier si l'utilisation de cette fonction présente des problèmes bloquants (notamment sur la pertinence de la distinction objet/primitive). J'ai déjà identifié quelques inconvénients, le principal étant joe.address !== undefined. Je pourrais contourner le problème par une propriété _isDefined mais ce n'est pas aussi élégant que le reste. Vous avez d'autres idées ? J'ai aussi pensé à permettre d'indiquer si le paramètre est facultatif ou non avec la notation age: [Number], pour imiter la syntaxe JSDoc tout en reposant sur les Array pour rester du JS valide. Mais il faut que je trouve autre chose pour indiquer les Array... et quid des Array typées ?

    Si quelques grosses têtes veulent m'aider à creuser le sujet, je fournis les pioches

  2. #2
    Expert confirmé
    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 : 36
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 094
    Par défaut
    J'aime bien l'idée mais il y a un truc qui me dérange :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    typeof Number(42) // "number"
    typeof new Number(42) // "objet"
    En utilisant new sur les types primitifs, tu les transformes en objets au lieu de les caster.

    Ça m'est arrivé de faire de la vérification de type (pour un pretty-printer si ma mémoire est bonne), et la méthode que j'avais choisie c'est de commencer par typeof, et quand ça me renvoie "object" je passe à constructor. Je trouve que la propriété constructor permet assez simplement d'approcher une notion de typage fort. En particulier si on prend soin de toujours nommer ses constructeurs, on peut utiliser constructor.name.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var tab = [];
     
    typeof tab // "object"
    tab.constructor // function Array()
    tab.constructor.name // "Array"
    Je te remercie de m'avoir fait découvrir les monades À la première lecture j'ai l'impression que c'est comme des promises, l'aspect asynchrone en moins, mais peut-être que je me trompe.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  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 : 74
    Localisation : Belgique

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

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

    Je viens de lire les documentations (merci de m'avoir fait découvrir des choses que je ne connaissais pas) et de tester tes codes.

    Le contrôle de la valeur et du typage d'une propriété de l'objet par un accesseur (get) et un mutateur (set) c'est du grand classique. Utiliser une seule fonction pour faire le travail au lieu de devoir le coder pour chacune des propriétés des objets c'est bien. Mais je me demande (je n'ai pas encore testé), que va-t-il se passer si je veux mes propres get/set pour gérer la valeur d'une propriété.

    Pour le reste, je ne suis pas convaincu de l'utilité de la chose. Il me semble que c'est employé un bazooka pour obtenir une valeur "undefined" au lieu de faire de la gestion d'erreur avec un simple try/catch.

    @Watilin: j'ai un peu testé les monades, c'est aussi un bazooka pour un résultat... j'en reviens toujours à un simple try/catch.

    Mon test :

    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
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    $( function(){
     
        try {
     
            var ObjectModel = function Object_Model( def ) {
     
                function wrap( obj ) {
                    if ( obj === null || obj === undefined ) {
                        return Object.create( null );
                    } else {
                        switch ( typeof obj ) {
                            case "string": return new String( obj );
                            case "number": return new Number( obj );
                            case "boolean": return new Boolean( obj );
                            case "object":
                            default: return obj;
                        }
                    }
                }
     
                function isPrimitive( def ) {
                    return def === String || def === Number || def === Boolean;
                }
     
                function safeProxy( obj, def ) {
                    var wrapper = wrap( obj ),
                        proxy = Object.create( Object.getPrototypeOf( wrapper ) );
     
                    if ( isPrimitive( def ) ) {
                        if ( obj !== undefined && def( obj ) !== obj ) {
                            throw new Error( "expecting " + def.name + ", got " + ( typeof obj ) );
                        }
     
                        return obj;
     
                    } else {
                        Object
                            .keys( def )
                            .forEach( function( key ) {
                                Object.defineProperty( proxy, key, {
                                    "get" : function(){
                                        return safeProxy( isPrimitive( def[ key ] ) && 
                                            obj instanceof Object ? obj[ key ] : wrapper[ key ], 
                                            def[ key ] );
                                    },
                                    "set" : function( val ){
                                        wrapper[ key ] = safeProxy( val, def[ key ] );
                                    }
                                });
                            });
                    }
     
                    return proxy;
                }
     
                return function( obj ) {
                    return safeProxy( obj, def );
                };
            };
     
            var Person = ObjectModel({
                "name" : String,
                "age" : Number,
                "female" : Boolean,
                "address" : {
                    "work" : {
                        "city" : String
                    }
                }
            });
     
            console.log( typeof Person ); // function
            console.log( Person ); // function ( obj ) { return safeProxy( obj, def ); }
     
            var joe = Person();
     
            console.log( typeof joe ); // object
            console.log( joe );
     
            /*
             * Object { name: (...), age: (...), female: (...), address: (...) }
             * 
             * address: (...)
             * get address: function (){
             * set address: function ( val ){
             *     
             * age: (...)
             * get age: function (){
             * set age: function ( val ){
             * 
             * female: (...)
             * get female: function (){
             * set female: function ( val ){
             * 
             * name: (...)
             * get name: function (){
             * set name: function ( val ){
             */
     
            joe.name = "Joe";
            joe.age = 24;
     
            console.log( joe.female ); // undefined
            console.log( joe.address.work.city ); // undefined
     
            joe.address = null;
     
            console.log( joe.address.work.city ); // undefined !  
     
        }
        catch( err ){
            console.log( err.message );
        }
     
        try {
     
            var person = {};
     
            person.address = {};
            person.address.work = {};
            person.address.work.city = "Hello";
     
            person.address = null; //"Cannot read property 'work' of null"
     
            person.address = {}; // "Cannot read property 'city' of undefined"
     
            console.log( person.address.work.city );
     
        }
        catch( err ){
            console.log( err.message );
        }
     
     
    });

    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
    Par défaut
    @Watilin: en fait, je cherche bien à les transformer et non à les caster (puisque justement je fais de la validation de type). Je me suis servi d'un wrapper sur les primitives pour résoudre mon problème initial, à savoir pouvoir faire ça :
    if(joe.address.work.city){ ... } ; sans risquer d'exceptions "Cannot read property" même si un objet parent vaut null ou est une primitive (par exemple joe.address="1 rue de la paix";). Mais je ne passe pas par les wrappers dans les getters des valeurs qui sont, d'après le modèle, sensées être des primitives. Donc j'ai bien typeof joe.age === "number".
    Le problème avec la propriété constructor est que c'est assez courant de la redéfinir sans faire attention (j'avais pris l'exemple de la classe Car dans un autre topic, il suffit d'un clio.constructor = "Renault" pour potentiellement casser des fonctions internes de bibliothèques/frameworks reposant sur cette propriété. Mieux vaut passer par instanceof à mon avis.

    @danielhagnoul: je dirais plutôt que c'est le try/catch qui est le bazooka Ou plutôt le mur anti-bazooka. L'énorme problème du try/catch, c'est qu'il intercepte tout sans exception. Ce qui fait qu'on doit tester l'erreur et la renvoyer à nouveau si ce n'est pas celle attendue.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    try {
    if(joe.address.work.city){ 
      doSomethingWith(joe.address.work.city);
    } else {
      doSomethingElse();
    }
     
    } catch(e){
      if(e instanceof TypeError){
         doSomethingElse();
      } else {
         throw e; //c'est une autre erreur imprévue, on la renvoie au-dessus
      }
    }
    Outre le fait que ce soit pénible de mettre des try/catch partout et de dupliquer une partie du code dans la branche du catch, c'est également très risqué. Car autant on a prévu à l'avance de potentielles TypeError pour ce if, autant il peut y avoir d'autres TypeError non prévues venant des fonctions doSomethingWith ou doSomethingElse ; non seulement elles ne seront pas remontées en console, mais elles exécuteront un code non prévu.

  5. #5
    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 : 74
    Localisation : Belgique

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

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Billets dans le blog
    125
    Par défaut
    Je suis en train de lire flow : A STATIC TYPE CHECKER FOR JAVASCRIPT, malheureusement c'est pour Mac et Linux uniquement.

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

  6. #6
    Membre chevronné
    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
    Par défaut
    Bonjour,

    Vous traitez plusieurs choses différentes dans votre code :
    1- l'accés à des données ayant une structure arborescente éventuellement absente pour partie,
    2- le contrôle des informations stockées dans cette structure.

    Pour le deuxième aspect, utiliser les constructeurs est un raccourci interessant mais limité.
    Il est, tel quel, difficile de l'utiliser pour une date. Peut-être que passer par une hiérarchie de classes ou d'objets faciliterait l'intégration de types de données plus ou moins complexes.

    Par 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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    function DataType ( type ) {
        if ( typeof type != "undefined" )
            this.type = Object(type);
    };
    DataType.prototype.type  = null;
    DataType.prototype.check = function check ( value ) { return false; };
     
    function PrimitiveType ( type ) {
        DataType.call(this, type);
        if ( typeof type == "function" )
            this.name = type.name;
    };
    PrimitiveType.prototype = new DataType();
    PrimitiveType.prototype.name  = "PrimitiveType";
     
    PrimitiveType.prototype.check = function check ( value ) {
        return this.type(value) === value;
    };
     
    var StringType  = new PrimitiveType(String);
    var BooleanType = new PrimitiveType(Boolean);
    var NumberType  = new PrimitiveType(Number);
     
    var Person = ObjectModel({ 
      name: StringType,
      age: NumberType,
      female: BooleanType,
      // ...
    });
    En changeant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function isPrimitive(def) {
        return def === String || def === Number || def === Boolean;
    }
    Par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function isDataType(def) {
        return def instanceof DataType;
    }
    Et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
          if ( isPrimitive(def) ) {
                if ( obj !== undefined && def(obj) !== obj ) {
                    throw new Error("expecting " + def.name + ", got " + (typeof obj));
                }
                return obj;
            }
    Par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
          if ( isDataType(def) ) {
                if ( obj !== undefined && ! def.check(obj) ) {
                    throw new Error("expecting " + def.name + ", got " + (typeof obj));
                }
                return obj;
            }
    Je parlais de date, parce que je m'attendais plutot à avoir un champ 'birthdate' représentant une date donc et un champ 'age' qui serait un champ calculé.
    Une question est : est-ce que les champs calculés font partie de la structure de données ou pas ?
    J'ai supposé que non. Dans ce cas, on peut l'inclure dans le prototype de la classe 'Person' ce qui implique de petits changements :

    Rajouter le type correspondant à la date

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function ObjectType ( type, name ) {
        DataType.call(this, type);
        if ( arguments.length < 2 ) {
            if ( typeof type == "function" )
                this.name = type.name;
        } else {
            this.name = String(name);
        }
    };
    ObjectType.prototype = new DataType();
    ObjectType.prototype.name = "Object";
     
    var DateTimeType = new ObjectType(Date);
    DateTimeType.check = function check ( value ) {
        if ( value == null )
            return false;
        if ( value instanceof Date )
            return true;
        return /^((\d{1,2})\/(\d{1,2})\/(\d{4}))?\s*((\d{1,2}):(\d{2}):(\d{2}))?$/.exec(String(value));
    };
    NB : le type décrit n'est pas bon, dans la mesure où ce qui est réellement stocké peut-être un objet Date ou une chaine de caractère. Il faudrait étendre DataType avec une méthode de normalisation de la valeur stockée.

    S'assurer que dans 'ObjectModel', la fonction se comportera comme un constructeur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      return function (obj) {
            return safeProxy(obj, def);
        };
    serait remplacé par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
      return function (obj) {
            var Ctor = arguments.callee;
            if ( this instanceof Ctor )
                return safeProxy(this, def);
            if ( obj instanceof Ctor )
                return safeProxy(obj, def);
            return new Ctor(); //?
        };
    'Person' deviendrait

    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
    var Person = ObjectModel({ 
      name: StringType,
      birthdate: DateTimeType,
      female: BooleanType,
      address: {
        length: NumberType,
        work: {
          city: StringType
        }
      }
    });
    Object.defineProperty(Person.prototype,"age",{
        get:function(){// un peu à l'arrache ...
            if ( this.birthdate )
                return (new Date()).getUTCFullYear()
                       - (this.birthdate instanceof Date 
                          ? this.birthdate 
                          : new Date(String(this.birthdate)) ).getUTCFullYear();
            return undefined;
        }
    });
    Pour finir, 'joe' devient

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    var joe = new Person();
    joe.name = "Joe";
    joe.birthdate = "1/12/2000";
    // joe.age == 14
    Pour le premier point, l'accés à une partie de structure absente, là encore la solution est plutot interessante. Mais je me suis fait piégé par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    joe.address.work.city = "limit";
    l'affectation ne fonctionne pas. Ce qui est logique mais qui m'a fait me poser la question de l'utilisation de la structure de données.

    Comment affecte-t-on une valeur à 'joe.address.work.city' ?

    Quand à savoir, si une partie de l'arborescence est valide, peut-être que l'ajout d'un propriété 'undefined' à 'true' pourrait faire l'affaire.
    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
      function wrap(obj) {
            if (obj === null || obj === undefined) {
                obj = Object.create(null);
                obj.undefined = true;
                return obj;
            } else switch (typeof obj) {
                case "string":
                    return new String(obj);
                case "number":
                    return new Number(obj);
                case "boolean":
                    return new Boolean(obj);
                default:
                    return obj;
            }
        }
    Cela permettrait de tester

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if ( jon.address.undefined )
        // ...
    Puisque lors de l'affectation, 'address' serait remplacé par un objet n'ayant pas cette propriété 'undefined' (ou 'isUndefined' ou ...)

    Enfin, parce que je m'amuse plutot en ES3, j'aurais utilisé soit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Ø = {}; // Ø = Object.freeze(Object.create(null));
     
    if ( (((jon || Ø).address || Ø).work || Ø).city != null )
        // ...
    Soit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if ( getValue(joe,"address.work.city") != null )
        // ...
    En espérant que ces quelques remarques contribueront à votre interessante reflexion.

Discussions similaires

  1. Utilisation de l'api criteria pour des join
    Par hugo123 dans le forum JPA
    Réponses: 0
    Dernier message: 04/02/2011, 11h46
  2. Réponses: 1
    Dernier message: 31/12/2009, 14h31
  3. Inlining des getters / setters auto avec GCC?
    Par _skip dans le forum Débuter
    Réponses: 45
    Dernier message: 17/08/2009, 12h51
  4. [Template] Changer la génération des getter/setter
    Par anthyme dans le forum NetBeans
    Réponses: 2
    Dernier message: 05/07/2007, 09h26
  5. Réponses: 1
    Dernier message: 31/05/2007, 11h57

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