Creating get/set method dynamically in javascript
I am trying to create a UserDon object, and trying to generate the get and set methods programmatically ( based on Pro Javascript book by John Resig page 37 ), and am testing this o开发者_如何学JAVAn Firefox 3.5
The problem is: in function UserDon, "this" refers to the window object instead of the UserDon object.
So after calling var userdon = new UserDon(...) I got setname and getname methods created on the window object (also setage and getage).
How can I fix this?
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
});
The this
value you are using belongs to the auto-invoking function expression you have inside the loop, and when you invoke a function in this way, this
will always refer to the global object.
Edit: I missed the fact that the function expression is trying to make variable capturing to handle the getter/setter creation inside the loop, but the looping variable i
, needs to be passed as an argument in order to do it and since the function expression is there, context (the outer this
) should be preserved:
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
You can also use the call
method to invoke the function expression, preserving the context (the value of this
) and introducing the looping variable to the new scope in a single step:
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
}
}
I would also recommend to use an if (properties.hasOwnProperty(i)) { ... }
inside the for...in
loop to avoid iterating over user extended properties inherited from Object.prototype
.
You can also use the lesser-known
__defineGetter__("varName", function(){});
and __defineSetter__("varName", function(val){});
Although they are nonstandard [like x-html-replace content-type] they are supported by a majority of the non-ie browsers out there [chrome, firefox]
Syntax would be:
benjamin = new object();
benjamin.__defineGetter__("age", function(){
return 21;
});
Or you can approach this with prototyping
benjamin = {
get age()
{
return 21;
}
}
It could be a better idea to get a generic function with 2 parameters : the name of the property and the value (or only the name for the getter). And this function would check the presence of a speial function for this property and if there isn't any, it would juste change the property's value (or return it's value for getter).
Here is how I would code it:-
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);
}
}
精彩评论