在Javascript中创建Getter和Setter

3

我正在学习如何在Javascript中创建getter和setter,我的代码如下:

// Create a new User object that accept an object of properties
function User(properties) {
    // Iterate through the properties of the object, and make
    // sure it's properly scoped
    for (var i in properties) { (function(){
        // Create a new getter for the property
        this['get' + i] = function() {
            return properties[i];
        };

        // Create a new setter for the property
        this['set' + i] = function(val) {
            properties[i] = val;
        };
    })(); }
}

// Create a new User object instance and pass in an object of
// properties to seed it with
var user = new User({
    name: 'Bob',
    age: 28
});

// Just note that the name property does not exist, as it's private
// within the property object
console.log(user.name == null);

// However, we are able to access its value using the new getname()
// method, that was dynamically generated
console.log(user.getname());

然而,控制台显示错误,说用户没有getname方法。代码试图动态生成getter和setter方法,我认为看起来没问题。您有什么想法吗?


你的循环函数正在丢失 this,请在循环外部执行 var t = this; 并在内部引用 t - Paul S.
2
你也可以使用es5 getters/setters - beatgammit
1
你的for循环中为什么要使用匿名函数?它有什么作用?你是从Web服务中获取数据吗? - beatgammit
@Shawn31313 - 啊,我想我忽略了getter在做什么。我不太明白为什么properties被隐藏了,但这并不是相关的。 - beatgammit
我也给了你我的getter/setter选项,以便完整性。 - beatgammit
显示剩余5条评论
3个回答

15
其他答案是正确的,因为你需要将传递到匿名函数中,但你也可以使用ES5的存取器来实现这一点:
// Create a new User object that accept an object of properties
function User(properties) {
    var self = this; // make sure we can access this inside our anon function
    for (var i in properties) {
        (function(i) {
            Object.defineProperty(self, i, {
                // Create a new getter for the property
                get: function () {
                    return properties[i];
                },
                // Create a new setter for the property
                set: function (val) {
                    properties[i] = val;
                }
            })
        })(i);
    }
}

使用ES5的getter和setter的好处是,现在您可以这样做:

var user = new User({name: 'Bob'});
user.name; // Bob
user.name = 'Dan';
user.name; // Dan

因为它们是函数,它们修改传递进来的properties,而不仅是对象本身。您不必再使用getNsetN,而可以直接使用.N,这使得使用看起来更像是访问对象上的属性。

然而,这种方法并不是普遍可移植的(需要IE 9+)。

这是我在实践中可能会做的事情:

function User(properties) {
    Object.keys(properties).forEach(function (prop) {
        Object.defineProperty(this, prop, {
            // Create a new getter for the property
            get: function () {
                return properties[prop];
            },
            // Create a new setter for the property
            set: function (val) {
                properties[prop] = val;
            }
        })
    }, this);
}

上面的代码去掉了你的for循环。你已经在使用匿名函数,所以最好充分利用它。


一个很好的副作用是你不能意外地赋值超过顶部:user.setname = 'Dan'。这并不是一个大问题,但我见过人们不小心这样做,然后想知道为什么他们的代码出错了。 - beatgammit
我已经添加了我认为更好的风格。for .. in可能会导致奇怪的问题(虽然在这种情况下可能不会),而且我认为Array.forEach更优雅。 - beatgammit
@tjameson ES5的getter和setter非常好用,但如果你只是在做镜像,那么与其使用_Object_,我不认为有什么意义。 - Paul S.
@PaulS。如果您想稍后批量使用属性(例如上传到服务器),而不允许附加额外的属性,则此选项可能很有用。也许OP在“用户”上还有其他仅在Web应用程序中有意义而不在潜在的POST到服务器中的属性。但我同意,OP没有说明为什么他/她想要这样做。 - beatgammit

2

可能是一个闭包问题:

function User(properties) {
    // Iterate through the properties of the object, and make
    // sure it's properly scoped
    for (var i in properties) { 
        (function(i){
            // Create a new getter for the property
            this['get' + i] = function() {
                return properties[i];
            };

            // Create a new setter for the property
            this['set' + i] = function(val) {
                properties[i] = val;
            };
        }.call(this, i));
    }
}

此外,正如@Paul所指出的那样,this实际上是指包含在for循环中的函数,而不是User函数。我通过使用调用并将User this分配给函数来修复了这个问题(不需要额外的变量)。

谢谢。这看起来非常优雅。但我不明白为什么使用call会起作用?'call'的本质是什么? - Rex
call 允许您定义 this.call(this, i) 中的 call 函数中的 this 是指用户函数的 this,然后将其传递给该函数。这相当于使用 .bind(this)(i)。唯一的区别是 call 触发函数并允许您在函数内设置参数。 - Shawn31313
这是一个与闭包有关的问题。我很难解释闭包。如果你不将 i 传递给调用函数以在函数中使用它,那么 i 将始终等于对象中的最后一个属性。 - Shawn31313
我认为匿名函数已经处理了“最后一个i”的问题。 - Rex
好的,不,除非你将 i 作为参数传递,否则它不会修复它。不传递参数:http://jsfiddle.net/shawn31313/zU7cE/。传递参数:http://jsfiddle.net/shawn31313/zU7cE/1/。 - Shawn31313

1
你的循环函数失去了 this,在循环外部使用 var t = this;,然后在内部引用 t。同时,将 i 传递到你的函数中。
function User(properties) {
    var t = this, i;
    for (i in properties) (function (i) {
        t['get' + i] = function () { return properties[i]; };
        t['set' + i] = function (val) { properties[i] = val; };
    }(i));
}

这是一个很棒的解决方案。谢谢。 - Rex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接