Reputation: 930
At the moment I am using the following code to define getter and setters for my class:
Object.defineProperty(FPProject.prototype, 'name', {
get: function() { return this.get('name'); },
set: function(aValue) {return this.set('name', aValue);}
});
Object.defineProperty(FPProject.prototype, 'code', {
get: function() { return this.get('code'); },
set: function(aValue) {return this.set('code', aValue);}
});
Object.defineProperty(FPProject.prototype, 'clientName', {
get: function() { return this.get('clientName'); },
set: function(aValue) {return this.set('clientName', aValue);}
});
Object.defineProperty(FPProject.prototype, 'client', {
get: function() { return this.get('client'); },
set: function(aValue) {return this.set('client', aValue);}
})
I thought I could optimise this code to something like this :
var fields = ['name','code','clientName','client'];
for (var i = 0; i < fields.length; i ++ ) {
Object.defineProperty(FPProject.prototype, fields[i], {
get: function() { return this.get(fields[i]); },
set: function(aValue) {return this.set(fields[i], aValue);}
});
}
but it doesn't work! I get no console errors, just cant set the properties... ???
Upvotes: 1
Views: 2156
Reputation: 1
Faced same problem, solved by just adding this
Object.defineProperty(FPProject.prototype, 'name', {
get: function() { return this.get('name'); },
set: function(aValue) {return this.set('name', aValue);},
enumerable: true, // <--
configurable: true // <--
});
Upvotes: 0
Reputation: 707466
The other two answers work just fine (and I upvoted them), but I thought I'd add that this is one place where an array iterator method comes in handy because the very task of using it creates a function closure for your operation which solves this index problem for you automatically:
['name','code','clientName','client'].forEach(function(item) {
Object.defineProperty(FPProject.prototype, item, {
get: function() { return this.get(item); },
set: function(aValue) {return this.set(item, aValue);}
});
});
And, of course, you have to pay attention to browser compatibility of .forEach()
which requires IE9 or greater or a polyfill (shown here).
FYI, this design pattern is used by a popular third party library because it's a very compact way of running the same code with several different values run through it.
Upvotes: 4
Reputation: 1074505
This is the classic closure problem. The functions you create in your loop have an enduring reference to the i
variable, not a copy of it. So by the time your accessor functions are called, i
is fields.length
and so the value they get from fields[i]
is undefined
.
The usual solution is a builder function:
var fields = ['name','code','clientName','client'];
for (var i = 0; i < fields.length; i ++ ) {
buildProperty(FPProject.prototype, fields[i]);
}
function buildProperty(obj, name) {
Object.defineProperty(obj, name, {
get: function() { return this.get(name); },
set: function(aValue) {return this.set(name, aValue);}
});
}
I always make this a nice, clear, separate function so that A) We're not recreating it on every loop, and B) It's easier to debug, understand, and reuse.
Now the accessors close over the context of the call to buildProperty
and its obj
and name
arguments, which don't change, and so when they're called they use the correct name
.
Upvotes: 3
Reputation: 48972
When your loop finishes, the i
variable will be equal to fields.length
, your set
function called later will use this value.
Try using closure to capture the current index element:
for (var i = 0; i < fields.length; i ++ ) {
(function (index) {
Object.defineProperty(FPProject.prototype, fields[index], {
get: function() { return this.get(fields[index]); },
set: function(aValue) {return this.set(fields[index], aValue);}
});
}(i));
}
Upvotes: 3