目录
- 属性描述符
- 数据属性描述符
- writable
- enumerable
- configurable
- value
- 存取属性描述符
- get
- set
- 关于get与set
- 通过Object.defineProperty实现响应式
属性描述符
在ES5
之前,我们虽然能通过字面量
的形式直接在对象上添加或修改属性,但终究不能对其进行更加精细的管理,如此属性是否可重写
,可否被枚举
等等
于是在ES5
中推出了属性描述符
,属性描述符
用于描述一个对象上指定的属性
的特性或功能,属性描述符
本身也是一个对象
属性描述符
具体可分为两类,一类为数据属性描述符
,一类为存取属性描述符
我们可以通过Object.getOwnPropertyDescriptor
来获取一个指定对象
上指定属性
的属性描述符
var obj = {
name: '张三',
age: 18
}
let desc = Object.getOwnPropertyDescriptor(obj, 'name')
console.log(desc)
Object.getOwnPropertyDescriptor
需要传入两个参数,一个是指定的对象
,一个是对象上的属性名
,返回一个对象
如果想要设置属性描述符
的话则需要使用Object.defineProperty
var obj = {
name: '张三',
age: 18
}
Object.defineProperty(obj, 'name', {
value: '李四',
})
let desc = Object.getOwnPropertyDescriptor(obj, 'name')
console.log(desc)
Object.defineProperty
需要传入三个参数,第一个是指定的对象
,第二个是指定的属性名
,第三个就是该属性的属性描述符
数据属性描述符
假如我们有这么一个对象,这个对象存储着一个商品的信息,其中name
为商品名,price
为商品售价,purchase
为进价,count
为商品的数量
var goods = {
name: "apple",
price: 10,
count: 10,
purchase: 5
}
writable
现在我们希望name
属性不能被重写,我们就可以通过属性描述符
实现
Object.defineProperty(goods, "name", {
writable: false
})
writable
即表示此属性能否被重写,如果为false
就不能被重写
goods.name = "banana"
console.log(goods.name)
我们可以看到赋值无效
enumerable
现在我们觉得purchase
这个属性并不适合展示出来,我们就可以这么修改属性描述符
Object.defineProperty(goods, "purchase", {
enumerable: false
})
enumerable
即表示此属性能否被for...in
和Object.keys
遍历到,如果为false
则遍历不到
for (const key in goods) {
console.log(key)
}
console.log(Object.keys(goods))
configurable
现在我们觉得price
这个属性已经配置的很好了,希望以后这个属性的属性描述符
不会再改变,我们就可以这么修改属性描述符
Object.defineProperty(goods, "price", {
configurable: false
})
configurable
即表示这个属性的属性描述符在此次修改之后能否再次被修改
,如果为false
则此次修改之后之后再无法修改此属性的属性描述符
Object.defineProperty(goods, "price", {
configurable: true
})
我们发现如果想再次修改属性描述符的话控制台会直接报错
value
最后再来谈谈value
属性,value
就是此属性的值
,嗯,没了
存取属性描述符
我们还是有一个商品对象
var goods = {
name: "apple",
price: 10,
count: 10,
purchase: 5
}
现在我们同样需要price
属性不能被修改,不仅不能被修改,还需要在修改的时候报错,这时候我们如果仅仅使用数据属性描述符
是无法实现的,需要使用存取属性描述符
get
get
也被称为getter
,如果配置了get
方法,那么当用户访问此属性时会调用get
方法,如果没有配置get
则为undefined
var price = goods.price
Object.defineProperty(goods, "price", {
get() {
return price
}
})
set
set
也被称之为setter
,如果配置了set
方法,那么当用户设置此属性时会调用set
方法,如果没有配置set
则为undefined
,set
方法会有一个参数,这个参数就是你设置此属性时传递的值
,如
goods.price = 2
这里的2
就会被传入set
var price = goods.price
Object.defineProperty(goods, "price", {
set(val) {
price = val
}
})
set
和get
合起来被称之为访问器
,无论是get
还是set
,他们都是对象属性上的一种绑定机制
,这个机制
能让函数与属性关联起来
,无论这个属性
在之前是否有定义,或是否有值,在定义了get
和set
的那一刻,它就成了一个特殊的属性
现在我们回到最开始的问题,如果我们希望price
不能被修改并且在尝试修改price
时会报错的话就可以这么写
var price = goods.price
Object.defineProperty(goods, "price", {
get() {
return price
},
set(val) {
throw new Error("不允许修改价格")
}
})
console.log(goods.price)
goods.price = 20
console.log(goods.price)
结果
关于get与set
在get
与set
中不能访问当前设定的属性
,如果访问了的话会无限递归
,直到撑爆执行栈
,强制报错
var price = goods.price
Object.defineProperty(goods, "price", {
get() {
return goods.price
},
set(val) {
throw new Error("不允许修改价格")
}
})
console.log(goods.price)
goods.price = 20
console.log(goods.price)
通过Object.defineProperty实现响应式
具体可以看我这篇文章
未动笔,未来可寄