本文详细介绍JavaScript中Getters和Setters的使用方法与平台支持情况。通过示例展示如何定义访问器属性、隐藏内部状态,并对比传统方法与__defineGetter__及新语法的区别。文章还涵盖了Firefox、Safari等浏览器的兼容性列表,以及对象继承中处理Getters和Setters的注意事项。
JavaScript Getters和Setters概述
本文基于John Resig早期的文章进行大致翻译,旨在作为技术备忘。
令人欣慰的是,如今JavaScript中的Getters和Setters已得到广泛应用,这与每位JavaScript开发者的日常工作息息相关。为了表达这一观点,作者等待已久。
首先,我们将快速了解Getters和Setters的概念及其重要性,随后探讨当前支持该特性的主要平台。
Getters和Setters
Getters和Setters的基本用法
Getters和Setters允许你快速获取或设置对象的数据。通常,一个对象包含两个方法,分别用于获取和设置某个值,例如:
代码如下:
{
getValue: function(){
return this._value;
},
setValue: function(val){
this._value = val;
}
} 使用这种方式编写JavaScript的一个显著优势是:它可以隐藏那些不希望被外部直接访问的属性。最终的代码结构如下(利用闭包保存新创建的Field对象的value):
代码如下:
function Field(val){
var value = val;
this.getValue = function(){
return value;
};
this.setValue = function(val){
value = val;
};
} 因此,我们可以这样使用:
代码如下:
var field = new Field("test");
field.value
// => undefined
field.setValue("test2")
field.getValue()
// => "test2" 使用__defineGetter__和__defineSetter__
为了模拟上述示例中“隐藏的value属性”,我们的代码可以写成这样:
代码如下:
function Field(val){
var value = val;
this.__defineGetter__("value", function(){
return value;
});
this.__defineSetter__("value", function(val){
value = val;
});
} Prototype中定义Getters和Setters
如果你不喜欢这种写法,而倾向于在对象的prototype中定义getters和setters(私有变量的位置并不重要),可以使用另一种语法。
代码如下:
function Field(val){
this.value = val;
}
Field.prototype = {
get value(){
return this._value;
},
set value(val){
this._value = val;
}
}; 这种语法初看可能令人惊讶,但使用一段时间后,接受它变得很容易。
Getters和Setters的高级应用与继承
接下来是另一个示例,它允许外界获取一个username数组,但无法获取原始的、隐藏的user对象。
代码如下:
function Site(users){
this.__defineGetter__("users", function(){
// JS 1.6 Array map()
return users.map(function(user){
return user.name;
});
};
} 作为补充,我编写了一个方法,用于实现对象的继承,并考虑到了getters和setters的情况。
代码如下:
// Helper method for extending one object with another
function extend(a,b) {
for ( var i in b ) {
var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
if ( g || s ) {
if ( g )
a.__defineGetter__(i, g);
if ( s )
a.__defineSetter__(i, s);
} else
a[i] = b[i];
}
return a;
} 在我的extend()方法中,你会看到两个新方法:__lookupGetter__和__lookupSetter__。一旦你开始真正使用getters和setters,这些方法将非常有用。
例如,当我第一次编写extend()方法时,遇到了各种错误,让我感到困惑。后来我发现问题出在一个简单的语句上:a[i] = b[i];
如果对象a存在一个名为i的setter,而对象b存在一个名为i的getter,a[i]的值并非通过a的setter方法赋值,而是来自b的getter方法计算出的值。这两个__lookup*__方法允许你访问用于这些方法的原始函数(从而允许你编写有效的extend方法,例如)。
If a setter existed in object a, named i, and a getter existed in object b, named i, a[i]'s value was being set not to the other setter function, but to the computed value from b's getter function. The two __lookup*__ methods allow you to access the original functions used for the methods (thus allowing you to write an effective extend method, for example).
记住以下几点:
注意事项与平台支持
一个对象内,每个变量只能有一个getter或setter。(因此value可以有一个getter和一个setter,但value绝没有两个getters)
删除getter或setter的唯一方法是:delete object[name]。delete可以删除一些常见的属性,getters和setters。
如果使用__defineGetter__或__defineSetter__,它会重写之前定义的相同名称的getter或setter,甚至是属性(property)。
平台
支持的浏览器有:
Firefox
Safari 3+
Opera 9.5
(原文没写Chrome,还没出呢)
我用下面的代码测试浏览器:
代码如下:
javascript:foo={get test(){ return "foo"; }};alert(foo.test); 另外,以下两种引擎也支持Getters和Setters:
SpiderMonkey
Rhino 1.6R6 (New)