可以循环属性以动态定义 getters/setters 吗?
Possible to loop over properties to dynamically define getters/setters?
假设如下:
var first = {
bob: null,
jim: null,
sue: null
},
second = {};
我希望能够遍历 first
的属性并为每个属性在 second
上定义 getters/setters;类似于以下内容:
for (var name in first) {
Object.defineProperty(second, ('' + name), {
get: function() {
return first[name];
},
set: function(value) {
first[name] = value;
}
});
}
问题是 getters/setters 似乎只影响迭代的最后一个 属性,在这种情况下 sue
.
但是,以下似乎工作得很好:
var second = {
get bob() {
return first['bob'];
},
set bob(value) {
first['bob'] = value;
},
get jim() {
return first['jim'];
},
set jim(value) {
first['jim'] = value;
},
get sue() {
return first['sue'];
},
set sue(value) {
first['sue'] = value;
}
};
我知道这一定是我遗漏的明显问题,但我似乎无法在这里找到另一个准确解决如何完成此问题的问题。
提前致谢!
如评论中所述,这是一个范围问题。
您的变量确实是 "hoisted",但这并不是真正的问题所在。这主要是因为变量 name
是全局作用域的一部分。在JavaScript中,作用域主要是函数。控制结构作为循环、条件...不声明新范围。
如果你这样做:
var first = {
bob: 'text',
jim: 42,
sue: true
},
second = {};
for (var name in first) {
Object.defineProperty(second, name, {
value: first[name],
enumerable: true
});
}
console.log(second.bob); // "test"
console.log(second.jim); // 42
console.log(second.sue); // true
它会起作用,因为 first[name] 是在循环的每次迭代中计算的。
现在如果你这样做:
var first = {
bob: 'text',
jim: 42,
sue: true
},
second = {};
for (var name in first) {
Object.defineProperty(second, name, {
get: function() {
return first[name];
},
set: function(value) {
first[name] = value;
},
enumerable: true
});
}
console.log(second.bob); // true
console.log(second.jim); // true
console.log(second.sue); // true
它不会起作用,因为 first[name] 仅在您调用 getter/setter 函数时计算。如果在循环之后你这样做: second.bob
将调用 getter 函数并且它不会在他自己的范围内找到名称。然后它将在父范围内搜索,这里是全局范围,其中定义了名称。循环结束,名称等于最后一次循环迭代。如果你做 second.bob = 'new value';
,它与 setter 相同。 setter 函数作用域没有 name
变量,将在名称为最后一次循环迭代的全局作用域中查找。
解决方案是使用let
或const
关键字将控制结构循环for
定义为变量name
的作用域。您还可以在循环外的函数中声明您的 definedProperty 过程,并且仅通过将参数 name
传递给她来在循环内调用此函数。在这种情况下,值将被复制并 'bound' 到新的函数范围。
// With 'let'
for (let name in first) {
Object.defineProperty(second, name, {
// ...
});
}
// With 'const'
for (const name in first) {
Object.defineProperty(second, name, {
// ...
});
}
// With function
var defineProp = function (name) {
Object.defineProperty(second, name, {
// ...
});
};
for (var name in first) {
defineProp(name);
}
假设如下:
var first = {
bob: null,
jim: null,
sue: null
},
second = {};
我希望能够遍历 first
的属性并为每个属性在 second
上定义 getters/setters;类似于以下内容:
for (var name in first) {
Object.defineProperty(second, ('' + name), {
get: function() {
return first[name];
},
set: function(value) {
first[name] = value;
}
});
}
问题是 getters/setters 似乎只影响迭代的最后一个 属性,在这种情况下 sue
.
但是,以下似乎工作得很好:
var second = {
get bob() {
return first['bob'];
},
set bob(value) {
first['bob'] = value;
},
get jim() {
return first['jim'];
},
set jim(value) {
first['jim'] = value;
},
get sue() {
return first['sue'];
},
set sue(value) {
first['sue'] = value;
}
};
我知道这一定是我遗漏的明显问题,但我似乎无法在这里找到另一个准确解决如何完成此问题的问题。
提前致谢!
如评论中所述,这是一个范围问题。
您的变量确实是 "hoisted",但这并不是真正的问题所在。这主要是因为变量 name
是全局作用域的一部分。在JavaScript中,作用域主要是函数。控制结构作为循环、条件...不声明新范围。
如果你这样做:
var first = {
bob: 'text',
jim: 42,
sue: true
},
second = {};
for (var name in first) {
Object.defineProperty(second, name, {
value: first[name],
enumerable: true
});
}
console.log(second.bob); // "test"
console.log(second.jim); // 42
console.log(second.sue); // true
它会起作用,因为 first[name] 是在循环的每次迭代中计算的。
现在如果你这样做:
var first = {
bob: 'text',
jim: 42,
sue: true
},
second = {};
for (var name in first) {
Object.defineProperty(second, name, {
get: function() {
return first[name];
},
set: function(value) {
first[name] = value;
},
enumerable: true
});
}
console.log(second.bob); // true
console.log(second.jim); // true
console.log(second.sue); // true
它不会起作用,因为 first[name] 仅在您调用 getter/setter 函数时计算。如果在循环之后你这样做: second.bob
将调用 getter 函数并且它不会在他自己的范围内找到名称。然后它将在父范围内搜索,这里是全局范围,其中定义了名称。循环结束,名称等于最后一次循环迭代。如果你做 second.bob = 'new value';
,它与 setter 相同。 setter 函数作用域没有 name
变量,将在名称为最后一次循环迭代的全局作用域中查找。
解决方案是使用let
或const
关键字将控制结构循环for
定义为变量name
的作用域。您还可以在循环外的函数中声明您的 definedProperty 过程,并且仅通过将参数 name
传递给她来在循环内调用此函数。在这种情况下,值将被复制并 'bound' 到新的函数范围。
// With 'let'
for (let name in first) {
Object.defineProperty(second, name, {
// ...
});
}
// With 'const'
for (const name in first) {
Object.defineProperty(second, name, {
// ...
});
}
// With function
var defineProp = function (name) {
Object.defineProperty(second, name, {
// ...
});
};
for (var name in first) {
defineProp(name);
}