目录
- 属性描述符
 - 数据属性描述符
 - 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实现响应式
具体可以看我这篇文章
 未动笔,未来可寄



















