Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
先看一个简单的示例:
let obj = {}
Object.defineProperty(obj, 'name', {
value: 'jsthin'
})
Object.defineProperty(obj, 'age', {
value: 18
})
console.log(obj) // {name: 'jsthin', age: 18}
可以看到obj对象中新增了两个属性(name,age)。
语法
Object.defineProperty(obj, prop, descriptor)
参数说明
- obj:要定义属性的对象
- prop:要新增或修改的属性名
- descriptor:要定义或修改的属性的描述符
{
value: 该属性对应的值,可以是Any类型,默认值undefined,
configurable: 该属性是否能被操作,比如删除该属性、重新定义属性,默认值false,
enumerable:该属性是否能被枚举,在for in遍历或Object.keys()中枚举,默认值false,
writable: 是否可写,即该属性的值是否可以修改,默认值false,
get: 该属性的getter函数,当访问该属性是,这个函数会被调用
set: 该属性的setter函数,当给该属性赋值时,这个函数会被调用,并将赋值的值作为参数传入
}
描述符
Object.defineProperty()方法有两种描述符,分别是数据描述符和存取描述符,并且不能混合使用。
数据描述符与存取描述符都有以下两个属性:configurable与enumerable
数据描述符
可以理解为由value,writable这两个属性组成的属性描述符
Object.defineProperty(obj, 'name', {
enumerable : true,
configurable : true,
value: 'jsthin',
writable: true // 是否可以通过属性定义的方式修改name属性
})
存取描述符
由set,get属性组成的属性描述符
let obj = {}
let bValue = 1
Object.defineProperty(obj, 'a', {
enumerable : true,
configurable : true,
get() {
return bValue
},
set(newVal) {
bValue = newVal
}
})
defineReactive
上述存取描述符的使用中,我们使用了一个全局变量bValue,去承载这个对象属性值的变化,如果使用Object.defineProperty维护多个属性,这种方式就会很冗余。于是就有了defineReactive(尤大牛逼),通过闭包作用域去维护这个变量,具体实现如下:
function defineReactive(data, key, val = data[key]) {
Object.defineProperty(data, key, {
enumerable : true,
configurable : true,
get() {
return val
},
set(newVal) {
if (newVal !== data[key]) {
val = newVal
}
}
})
}
defineReactive(obj, 'a', 10)
obj.a
obj.a = 2
console.log(obj.a) // 2