代码之家  ›  专栏  ›  技术社区  ›  William Bueti

是否可以循环使用属性来动态定义getter/setter?

  •  0
  • William Bueti  · 技术社区  · 7 年前

    假设如下:

    var first = {
        bob: null,
        jim: null,
        sue: null
    },
    second = {};
    

    我想能够循环一下 first 并在上定义getter/setter second

    for (var name in first) {
        Object.defineProperty(second, ('' + name), {
            get: function() {
                return first[name];
            },
            set: function(value) {
                first[name] = value;
            }
        });
    }
    

    问题是getter/setter似乎只影响迭代的最后一个属性(在本例中) 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;
        }
    };
    

    1 回复  |  直到 7 年前
        1
  •  1
  •   ElJackiste    7 年前

    正如评论中提到的,这是一个范围问题。

    您的变量确实是“提升的”,但这并不是真正的问题。这主要是因为变量 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函数将被调用,它在自己的作用域中找不到名称。然后它将在父范围中搜索,这里是全局范围,其中定义了名称。正在完成的循环,name等于最后一次循环迭代。如果你这样做的话,对二传手也是一样的 second.bob = 'new value'; . setter函数作用域没有 名称 变量并将在全局范围中查找,其中name是最后一个循环迭代。

    解决方案是定义控制结构循环 for 作为变量的作用域 名称 let const 关键词。您还可以在循环外部的函数中声明definedProperty进程,并仅通过传递参数在循环内部调用此函数

    // 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);
    }