属性描述符
1.概述
对象里目前存在的属性描述符有两种主要形式:
-
数据描述符:是一个具有值的属性,该值可能是可写的,也可能不是可写的。
-
存取描述符:是由
getter-setter
函数对描述的属性。
描述符必须是这两种形式之一;不能同时是两者。
2.可选键值
先来看看文档中的图片
2.1数据描述符的可选键值
从图中可以看到,只属于数据描述符的可选键值为 value 和 writable
value(默认值:undefined)
- 代表该属性的值,可以是JS中任意有效类型的值(对象,数值,字符串等),包括Symbol类型
writable(默认:false)
- 仅当该属性为
true
时,value
才能被赋值运算符改变。
2.2存取描述符的可选键值
从图中可以看到,只属于存取描述符的可选键值为 get 和 set
get
- 获取该属性时会触发的拦截行为。
方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象),如果没有getter
则为undefined
。
set
- 设置该属性时会触发的拦截行为。如果没有
setter
则为undefined
,
2.3数据描述符和存取描述符共有的可选键值
configurable(默认值:false)
- 只有当
configurable
的值为 true 时。该属性的属性描述符才可以被改变(删除)。
enumerable(默认值:false)
- 当
enumerable
为true
时,该属性为可枚举
3.使用属性描述符
目前有两种方法可以修改属性描述符
ES5.1 提供的Object.defineProperty
ES6 提供的Object.defineProperties
3.1.Object.defineProperty()
Object.defineProperty()
方法会直接在对象上定义一个新属性,或者修改一个对象的属性, 并返回这个对象。
三个参数Object.defineProperty(obj, prop, descriptor)
- obj:目标对象
- prop:要改变的属性
- descriptor:要改变或定义的属性描述符
看一个例子,设置一个数据描述符
var obj = {}
//设置一个数据描述符
Object.defineProperty(obj,'define',{
value: 10, //定义值
writable: true, //可写
enumerable: true, //可被枚举
configurable: true //可修改描述符
})
console.log(obj.define) // 10
上面代码用Object.defineProperty
定义了一个属性以及它自身的属性描述符,又因为描述符的定义。
所以这个属性是可以被枚举被修改和删除的。
注意:如果configurable
描述符为false
,之后再获取(定义)此属性的描述符会报错。
下面再来定义存取描述符
//写法一
var obj = {}
//设置一个存取描述符
Object.defineProperty(obj,'define',{
get: () => console.log('get拦截成功') ,
set: value => console.log('set拦截成功')
})
obj.define //get拦截成功
obj.define = 1 //set拦截成功,不会改变值
//写法二
var obj = {}
var descript = {
get: () => console.log('get拦截成功') ,
set: value => console.log('set拦截成功')
}
Object.defineProperty(obj,'define',descript)
obj.define //get拦截成功
obj.define = 1 //set拦截成功,不会改变值
注意:定义的拦截操作,不涉及原型。除非将目标对象指向原型。
若目标对象指向对象原型,因为继承关系,当在对象上进行存取操作时(不管是不是原型),也会被拦截
如果目标对象指向原型,那么拦截操作的返回值会被所有继承该原型的对象共享
可以通过将值存储在另一个属性中解决。通过 this
指向某个被访问和修改属性的对象。
function myclass() {
}
Object.defineProperty(myclass.prototype, "x", {
get() {
return this.stored_x;//this指向调用的目标对象,不会影响其他的实例
},
set(x) {
this.stored_x = x; //this指向调用的目标对象,不会影响其他的实例
}
});
var a = new myclass();
var b = new myclass();
//a 与 b 继承同一个原型
a.x = 1;
console.log(b.x); // undefined
由于 ES6 引入了 Symbol 数据类型,所以Object.defineProperty
方法是定义key
为Symbol
的属性的方法之一
3.2.Object.defineProperties()
bject.defineProperties()
方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。 跟
Object.defineProperty()`不同的是,该方法可以一次性操作多个属性
var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
//.....
});
4.查询属性描述符
Object.ownPropertyDescriptor(object, property)
使用其方法,可以查询到属性描述符。不做赘述