问题
This is my first attempt at using Javascript's Object.defineProperty
and/or defineProperties
and apparently I'm doing it completely incorrectly. I am passing a cfg object into a Javascript function constructor, then inside that constructor am looping over the config and
- Calling Object.defineProperty on each cfg key
- Assigning
this[key] = cfg[key]
However every this[key]
emerges with undefined
as it's value. I've tried a few different things, such as setting the writable
attribute to true, but got an error that this conflicts with the set/function
. I've also tried using Object.defineProperties
with the same result. I don't think it's a scoping issue with this
and have even used .bind(this)
on the setters to be sure, but to no avail. The code is can be run at https://repl.it/@dexygen/js-define-properties and I am including it below inline. Expected result of course rather than undefined
is that this.foo
e.g. will contain the string 'foo' from cfg.foo
passed into the object constructor function
let myObj = new ObjInstance({
foo: 'foo',
bar: 'bar',
baz: 'baz'
});
function ObjInstance(cfg) {
for (let key in cfg) {
Object.defineProperty(this, key, {
configurable: true,
enumerable: true,
// writable: true, // throws error in conjunction with set function
set: (function(val) {
console.log('this[' + key + ']: ' + this[key]); //undefined :(
}).bind(this)
});
console.log('cfg[key]: ' + cfg[key]);
this[key] = cfg[key];
}
console.log(this);
console.log('this.foo: ' + this.foo);
/* // if you comment this in, comment out the above for loop
let objProperties = {}
for (let key in cfg) {
objProperties[key] = {
configurable: true,
enumerable: true,
set: (function(val) {
console.log('this[' + key + ']: ' + this[key]);
}).bind(this)
}
}
for (let key in cfg) {
console.log('cfg[key]: ' + cfg[key]);
this[key] = cfg[key];
}
Object.defineProperties(this, objProperties);
for (let key in cfg) {
console.log('cfg[key]: ' + cfg[key]);
this[key] = cfg[key];
}
*/
}
回答1:
When you use setter or getter, then it cannot have the same property name as the returned property. It seems that you try to do something like this:
var obj = {
set a(param){
console.log(this.a); //setter try to set a setter
},
get a(){
return this.a; //RangeError: Maximum call stack
}
};
The property that is set or got by setter
or getter
must have different property name, eg _foo
:
let myObj = new ObjInstance({
foo: 'foo',
bar: 'bar',
baz: 'baz'
});
console.log(myObj);
function ObjInstance(cfg) {
for (let key in cfg) {
Object.defineProperty(this,key, {
configurable: true,
enumerable: true,
set:(val)=>{
this[`_${key}`] = key;
},
get:()=>{
return this[`_${key}`];
}
});
console.log('cfg[key]: ' + cfg[key]);
this[key] = cfg[key];
}
console.log(this);
console.log('this.foo: ' + this.foo);
}
or you can just store the values in the getter
s like this:
let myObj = new ObjInstance({
foo: 'foo',
bar: 'bar',
baz: 'baz'
});
console.log(myObj);
function ObjInstance(cfg) {
for (let key in cfg) {
Object.defineProperty(this,key, {
configurable: true,
enumerable: true,
get:()=>{
return key;
}
});
console.log('cfg[key]: ' + cfg[key]);
this[key] = cfg[key];
}
console.log(this);
console.log('this.foo: ' + this.foo);
}
回答2:
I want to thank the first person to comment but he's declined to answer so I am posting my own based not only on his comment (the fact that I had not providde a getter
more or less being the reason I was getting back undefined
from my setter
), but also addressing issues brought up by some of the other answers, one being the questioning of my use case, which I address with a comment in the setter
.
Working code here: https://repl.it/@dexygen/javascript-defineProperty-working
let myObj = new ObjInstance({
foo: 'foo',
bar: 'bar',
baz: 'baz'
});
function ObjInstance(cfg) {
for (let key in cfg) {
this[key] = cfg[key];
Object.defineProperty(this, key, {
configurable: true,
enumerable: true,
set: function(val) {
cfg[key] = val;
// additionally plan to emit an event
},
get: function() {
return cfg[key];
}
});
}
console.log('this.foo: ' + this.foo);
this.foo = 'foobarbaz';
console.log('this.foo: ' + this.foo);
}
回答3:
Here is an alternative JavaScript backward compatible equivalent
function Handle( O ) {
var arr = [];
for( var p in O ) {
arr.push(
["get "+p+"(){ return O['"+p+"']; }, set "+p+"(x){ O['"+p+"'] = x; /*your emitter*/ }" ]
);
}
return Function("O", "return {"+arr.join()+"}" )(O);
}
回答4:
Why is it that a nice and clean conventional JavaScript doesn't work for you?
var myObj = new ObjInstance
(
{
foo: 'foo',
bar: 'bar',
baz: 'baz'
}
);
function ObjInstance( cfg ) {
for ( var key in cfg ) {
this[ key ] = cfg[ key ];
}
};
console.log( JSON.stringify( myObj ) );
myObj.bar = 'bar2';
console.log( JSON.stringify( myObj ) );
来源:https://stackoverflow.com/questions/47367205/assigning-object-properties-in-a-loop-and-including-a-setter-results-in-undefi