可以循环属性以动态定义 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 变量,将在名称为最后一次循环迭代的全局作用域中查找。

解决方案是使用letconst 关键字将控制结构循环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);
}