2010-04-11 3 views
6

J'essaye de créer un objet UserDon, et de tenter de générer les méthodes get et set par programmation (basé sur le livre Pro Javascript de John Resig page 37), et de le tester sur Firefox 3.5Création dynamique de la méthode get/set en javascript

Le problème est: dans la fonction UserDon, "this" fait référence à l'objet window au lieu de l'objet UserDon. Donc, après avoir appelé var userdon = new UserDon (...) j'ai créé des méthodes setname et getname sur l'objet window (également setage et getage).

Comment puis-je résoudre ce problème?

function UserDon(properties) { 
    for(var i in properties) { 
    (function(){ 
     this[ "get" + i ] = function() { 
     return properties[i]; 
     }; 

     this[ "set" + i ] = function(val) { 
     properties[i] = val; 
     }; 
     })(); 
    } 
} 

var userdon = new UserDon({ 
    name: "Bob", 
    age: 44 
}); 
+1

si cela est un exercice.Sinon, si vous n'effectuez aucune opération supplémentaire dans les getters/setters, il est à espérer que vous utiliserez simplement les propriétés publiques et que vous renoncerez à la surcharge de la méthode. :) – deceze

Répondre

7

La valeur this que vous utilisez appartient à l'expression de fonction auto invocation que vous avez à l'intérieur de la boucle, et lorsque vous invoquez une fonction de cette façon, this sera toujours se référer à l'objet global.

Edit: J'ai raté le fait que l'expression de la fonction est essayer de faire la capture variable pour gérer la création getter/setter dans la boucle, mais la variable boucle i, doit être passé comme argument pour de le faire et comme l'expression de la fonction est là, le contexte (l'extérieur this) doivent être préservées:

function UserDon(properties) { 
    var instance = this; // <-- store reference to instance 

    for(var i in properties) { 
    (function (i) { // <-- capture looping variable 
     instance[ "get" + i ] = function() { 
     return properties[i]; 
     }; 

     instance[ "set" + i ] = function(val) { 
     properties[i] = val; 
     }; 
    })(i); // <-- pass the variable 
    } 
} 

var userdon = new UserDon({ 
    name: "Bob", 
    age: 44 
}); 

userdon.getname(); // "Bob" 
userdon.getage(); // 44 

Vous pouvez également utiliser la méthode call pour appeler l'expression de fonction, en préservant le contexte (la valeur de this) et introdu Cing la variable de boucle vers le nouveau champ d'application en une seule étape:

function UserDon(properties) { 
    for(var i in properties) { 
    (function (i) { // <-- looping variable introduced 
     this[ "get" + i ] = function() { 
     return properties[i]; 
     }; 

     this[ "set" + i ] = function(val) { 
     properties[i] = val; 
     }; 
    }).call(this, i); // <-- preserve context and capture variable 
    } 
} 

Je recommande également d'utiliser un if (properties.hasOwnProperty(i)) { ... } intérieur de la boucle pour éviter for...in itération sur les propriétés étendues de l'utilisateur héritées de Object.prototype.

+1

Sans la fonction d'invocation automatique, getname() et getage() retourneront 44, car closure référence la dernière valeur du tableau, qui est l'âge, référez-vous à la page 29 du livre. – portoalet

+0

@portoalet: Vous avez raison, j'ai manqué ce point, mais cela ne suffit pas, la variable 'i' devrait être passée en argument à cette fonction, aussi la valeur' this' devrait être préservée, voir mon edit. – CMS

+0

Merci, ça marche maintenant parfaitement. – portoalet

0

Cela pourrait être une meilleure idée d'obtenir une fonction générique avec 2 paramètres: le nom de la propriété et la valeur (ou seulement le nom du getter). Et cette fonction vérifierait la présence d'une fonction speial pour cette propriété et s'il n'y en a pas, elle changerait juste la valeur de la propriété (ou retournerait sa valeur pour getter).

0

Voici comment je coder: -

function UserDon(properties) { 
    var self = this; 
    for(var i in properties) { 
    (function(prop){ 
     self[ "get" + prop ] = function() { 
     return properties[prop]; 
     }; 

     self[ "set" + prop ] = function(val) { 
     properties[prop] = val; 
     }; 
     })(i); 
    } 
} 
1

Vous pouvez également utiliser le moins connu
__defineGetter__("varName", function(){});
et __defineSetter__("varName", function(val){});

Bien qu'ils soient non standard [comme x-html-remplacer content-type] ils sont supportés par la majorité des navigateurs non-ie là-bas [chrome, firefox]

La syntaxe serait:

benjamin = new object(); 
benjamin.__defineGetter__("age", function(){ 
    return 21; 
}); 

Ou vous pouvez aborder cela avec une bonne question prototypage

benjamin = { 
    get age() 
    { 
    return 21; 
    } 
} 
+0

n'a jamais vu ça avant. Utilisez-vous cela vous-même? – portoalet

+1

Je l'utilise uniquement pour le débogage où une variable est en train d'être mise en place. Vous définissez SetSetter ("variableName", outputStackTrace); et cela fonctionne aussi avec les styles CSS. Par exemple, vous dites 'object.style .__ defineGetter __ (" left ", outputStackTraceFunction);' Pour une raison quelconque, les gens ne connaissent pas cette fonction. Probablement parce que c'est non-standard? – Warty

Questions connexes