循环中的object.defineProperty

时间:2014-04-26 08:21:36

标签: javascript

目前我正在使用以下代码为我的班级定义getter和setter:

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

我认为我可以将此代码优化为以下内容:

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

但它不起作用!我没有控制台错误,只是无法设置属性...... ???

3 个答案:

答案 0 :(得分:3)

当你的循环结束时,i变量将等于fields.length,稍后调用的set函数将使用此值。

尝试使用闭包捕获当前索引元素:

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

答案 1 :(得分:3)

这是经典的闭包问题。您在循环中创建的函数对i变量具有持久引用,而不是它的副本。因此,当您的访问者函数被调用时,ifields.length,因此他们从fields[i]获得的值为undefined

通常的解决方案是构建器功能:

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

我总是把它做成一个漂亮,清晰,独立的功能,以便A)我们不会在每个循环中重新创建它,并且B)它更容易调试,理解和重用。

现在,访问者关闭了buildProperty及其objname参数调用的上下文,这些参数不会发生变化,因此当他们发生变化时他们称他们使用正确的name

答案 2 :(得分:3)

另外两个答案工作正常(我赞成它们),但我想我会补充说,这是一个数组迭代器方法派上用场的地方,因为使用它的任务创建了一个函数闭包自动解决此索引问题的操作:

['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);}
    });
});

当然,你必须注意.forEach()的浏览器兼容性,它需要IE9或更高版本或者polyfill(显示为here)。

仅供参考,这种设计模式由流行的第三方库使用,因为它是一种非常紧凑的方式,可以运行相同的代码,并通过它运行几个不同的值。