目录
getter和setter的作用
JavaScript中的对象支持通过getter和setter设置伪属性,该伪属性具有普通属性的使用方式,可以读取其值或对其赋值,但是在读取其值或对其赋值时,其内部调用的是getter函数以及setter函数,getter函数和setter函数是伪属性对应的读、写方法,可以在这两个方法中实现相关逻辑从而精确地控制读写该伪属性的行为。
添加伪属性的方式
JavaScript中有多种方式为对象添加伪属性。
1. 通过Object.prototype.__defineGetter__()
和 Object.prototype.__defineSetter__()
定义getter和setter
Object对象原型上具有__defineGetter__
和__defineSetter__
方法,通过这两个方法,对象就可以添加伪属性。
Object.prototype.__defineGetter__()
- 方法签名
obj.__defineGetter__(prop, func)
prop
: 一个字符串,表示指定的属性名。func
: 一个函数,当 prop 属性的值被读取时自动被调用。
- 方法描述
__defineGetter__
方法可以为一个已经存在的对象设置(新建或修改)访问器属性,可以将一个函数绑定在当前对象的指定属性上,当那个属性的值被读取时,你所绑定的函数就会被调用。 - 示例
const obj1 = { firstName: 'Jackie', lastName: 'Chan' }; obj1.__defineGetter__('fullName', function() { return this.firstName + ' ' + this.lastName; }); console.log(obj1.fullName); // 输出: 'Jackie Chan'
- 方法签名
Object.prototype.__defineSetter__()
-
方法签名
obj.__defineSetter__(prop, fun)
prop
: 一个字符串,表示指定的属性名。fun
: 一个函数,当试图去为 prop 属性赋值时被调用。通常你要给这个函数指定一个参数:function(val) { . . . }
- val: 任意的参数名,在 fun 被调用时,该参数的值就是尝试给 sprop 属性所赋的值。
-
方法描述
__defineSetter__
方法可以为一个已经存在的对象设置(新建或修改)访问器属性,可以将一个函数绑定在当前对象的指定属性上,当那个属性被赋值时,你所绑定的函数就会被调用。 -
示例
const obj1 = { firstName: '', lastName: '' }; obj1.__defineSetter__('fullName', function(v) { const names = v.split(' '); this.firstName = names[0]; this.lastName = names[1]; }); obj1.fullName = 'Jackie Chan'; console.log(obj1.firstName); // 输出: 'Jackie' console.log(obj1.lastName); // 输出: 'Chan'
-
2. 在新对象初始化时定义getter和setter
- getter
在新对象初始化时,可以通过get
关键字为对象添加一个getter,作为可读的伪属性。-
语法
{ get prop() { ... } } { get [expression]() { ... } }
prop
: 要绑定到给定函数的属性名。expression
: 从 ECMAScript 2015 开始,还可以使用一个计算属性名的表达式绑定到给定的函数。
-
描述
在新对象初始化时,可以通过get
关键字为对象添加一个getter,作为可读的伪属性。get
语法将对象属性绑定到查询该属性时将被调用的函数。使用get语法时应注意以下问题:
- 可以使用数值或字符串作为标识
- 必须不带参数(请参考Incompatible ES5 change: literal getter and setter functions must now have exactly zero or one arguments)
- 它不能与另一个 get 或具有相同属性的数据条目同时出现在一个对象字面量中(不允许使用 { get x() { }, get x() { } } 和 { x: …, get x() { } })
- 可通过
delete
操作符删除 getter,注意,通过delete操作符删除getter时,也会删除同名的setter
-
示例
// 普通示例 const obj1 = { firstName: 'Jackie', lastName: 'Chan', get fullName() { return this.firstName + ' ' + this.lastName; } }; console.log(obj1.fullName); // 输出: 'Jackie Chan' // 使用计算属性名的示例 const expr = 'foo'; const obj2 = { get [expr]() { return 'bar'; } }; console.log(obj.foo); // 输出: 'bar'
-
- setter
在新对象初始化时,可以通过set
关键字为对象添加一个setter,作为可写的伪属性。- 语法
{ set prop(val) { . . . } } { set [expression](val) { . . . } }
prop
: 要绑定到给定函数的属性名。val
: 用于尝试分配给prop的值。expression
: 从 ECMAScript 2015 开始,还可以使用一个计算属性名的表达式绑定到给定的函数。
- 描述
在新对象初始化时,可以通过set
关键字为对象添加一个setter,作为可写的伪属性。当尝试设置属性时,set
语法将对象属性绑定到要调用的函数。
使用 set 语法时请注意:- 它的标识符可以是数字或字符串
- 它必须有一个明确的参数 (详见 Incompatible ES5 change: literal getter and setter functions must now have exactly zero or one arguments)
- 在对象字面量中,不能为一个已有真实值的变量使用 set ,也不能为一个属性设置多个 set
( { set x(v) { }, set x(v) { } } 和 { x: …, set x(v) { } } 是不允许的 ) - setter 可以用
delete
操作来移除,注意,通过delete
操作符删除setter时,也会删除同名的getter
- 示例
// 普通示例 const obj1 = { firstName: '', lastName: '', set fullName(v) { const names = v.split(' '); this.firstName = names[0]; this.lastName = names[1]; } }; obj1.fullName = 'Jackie Chan'; console.log(obj1.firstName); // 输出: 'Jackie' console.log(obj1.lastName); // 输出: 'Chan' // 使用计算属性名的示例 const expr = 'foo'; const obj2 = { baz: 'bar', set [expr](v) { this.baz = v; } }; console.log(obj2.baz); // 输出: 'bar' obj2.foo = 'baz'; console.log(obj2.baz); // 输出: 'baz'
- 语法
3. 通过Object.defineProperty()和Object.defineProperties()定义getter和setter
-
Object.defineProperty()
静态方法Object.defineProperty(obj, prop, descriptor)
给我们提供了一种通用的方式让我们可以定义属性的元数据信息,从而精确地控制属性的行为,这里的元数据信息可以是set
和get
定义的存取器,具体可以参见另一篇博文Object.defineProperty()详解。- 语法
Object.defineProperty(obj, prop, descriptor)
- 参数
obj
: Object,Required,要在其上定义属性的对象。prop
: String|Symbol,Required,要定义或修改的属性的名称。descriptor
: Object,Required,将被定义或修改的属性描述符。
- 返回值: Object,返回被传递给函数的对象obj。
- 参数
- 描述
该方法允许精确添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法)
, 这些属性的值可以被改变,也可以被删除。方法Object.defineProperty()
允许修改默认的属性元数据配置。我们可以认为使用Object.defineProperty()
定义的属性在使用上更加严格。虽然Object.defineProperty()
功能比较强大,但此处我们重点关注使用该方法配置getter以及setter,该方法的详细信息具体可以参加上述博客。
descriptor
可以是数据描述符或者存取描述符,此处我们只关注存取描述符。当descriptor
是存取描述符时,可以配置以下可选键值:- get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。默认为 undefined。 - set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为 undefined。
- get
- 示例
const obj1 = { firstName: '', lastName: '' }; Object.defineProperty(obj1, 'fullName', { get: function () { return this.firstName + ' ' + this.lastName; }, set: function(v) { const names = v.split(' '); this.firstName = names[0]; this.lastName = names[1]; } }); obj1.fullName = 'Jackie Chan'; console.log(obj1.fullName); // 输出: 'Jackie Chan' console.log(obj1.firstName); // 输出: 'Jackie' console.log(obj1.lastName); // 输出: 'Chan'
- 语法
-
Object.defineProperties()
- 描述
静态方法Object.defineProperty(obj, prop, descriptor)
只能每次对一个属性设置元数据配置信息,即每次只能对一个属性设置getter和setter,为了能够对多个属性同时设置元数据配置信息,Object还提供了静态方法Object.defineProperties(obj, props)
。参数props是包含多个key-value的键值对对象,其中key是就是静态方法Object.defineProperty(obj, prop, descriptor)
中的 prop 参数,value就是静态方法Object.defineProperty(obj, prop, descriptor)
中的 descriptor 参数。 - 示例
const obj1 = { firstName: '', lastName: '', _job: '' }; Object.defineProperties(obj1, { fullName: { get: function () { return this.firstName + ' ' + this.lastName; }, set: function(v) { const names = v.split(' '); this.firstName = names[0]; this.lastName = names[1]; } }, job: { get: function() { return this._job.toString().toUpperCase(); }, set: function(v) { this._job = v; } } }); obj1.fullName = 'Jackie Chan'; console.log(obj1.fullName); // 输出: 'Jackie Chan' console.log(obj1.firstName); // 输出: 'Jackie' console.log(obj1.lastName); // 输出: 'Chan' obj1.job = 'web developer'; console.log(obj1.job); // 输出: 'WEB DEVELOPER'
- 描述
浏览器兼容性
__defineGetter__ 和 __defineSetter__ | 新对象初始化时定义getter和setter | Object.defineProperty() | Object.defineProperties() |
---|---|---|---|
主流现代浏览器以及IE11+支持该方法。需要注意的是,该方法是非标准方法,不属于任何规范,虽然大部分浏览器支持该方法,但应该使用标准规范中的其他方法来替代此方法。 | 主流现代浏览器以及IE9+支持,在标准规范ECMAScript 5.1 (ECMA-262)中被首次定义。 | 主流现代浏览器以及IE9+支持,IE8虽然实现了该方法,但只能在 DOM 对象上使用且存在诸多限制。该方法在规范ECMAScript Latest Draft (ECMA-262)中被定义。 | 主流现代浏览器以及IE9+支持,在规范ECMAScript Latest Draft (ECMA-262)中被定义。 |
参考
MDN defineGetter
MDN defineSetter
MDN getter
MDN setter
MDN Object.defineProperty()
MDN Object.defineProperties()