Object.defineProperty() 方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)對(duì)象的現(xiàn)有屬性,并返回此對(duì)象。
先看一個(gè)簡(jiǎn)單的示例:
let obj = {}
Object.defineProperty(obj, 'name', {
value: 'jsthin'
})
Object.defineProperty(obj, 'age', {
value: 18
})
console.log(obj) // {name: 'jsthin', age: 18}
可以看到obj對(duì)象中新增了兩個(gè)屬性(name,age)。
語(yǔ)法
Object.defineProperty(obj, prop, descriptor)
參數(shù)說(shuō)明
- obj:要定義屬性的對(duì)象
- prop:要新增或修改的屬性名
- descriptor:要定義或修改的屬性的描述符
{
value: 該屬性對(duì)應(yīng)的值,可以是Any類型,默認(rèn)值undefined,
configurable: 該屬性是否能被操作,比如刪除該屬性、重新定義屬性,默認(rèn)值false,
enumerable:該屬性是否能被枚舉,在for in遍歷或Object.keys()中枚舉,默認(rèn)值false,
writable: 是否可寫(xiě),即該屬性的值是否可以修改,默認(rèn)值false,
get: 該屬性的getter函數(shù),當(dāng)訪問(wèn)該屬性是,這個(gè)函數(shù)會(huì)被調(diào)用
set: 該屬性的setter函數(shù),當(dāng)給該屬性賦值時(shí),這個(gè)函數(shù)會(huì)被調(diào)用,并將賦值的值作為參數(shù)傳入
}
描述符
Object.defineProperty()方法有兩種描述符,分別是數(shù)據(jù)描述符和存取描述符,并且不能混合使用。
數(shù)據(jù)描述符與存取描述符都有以下兩個(gè)屬性:configurable與enumerable
數(shù)據(jù)描述符
可以理解為由value,writable這兩個(gè)屬性組成的屬性描述符
Object.defineProperty(obj, 'name', {
enumerable : true,
configurable : true,
value: 'jsthin',
writable: true // 是否可以通過(guò)屬性定義的方式修改name屬性
})
存取描述符
由set,get屬性組成的屬性描述符
let obj = {}
let bValue = 1
Object.defineProperty(obj, 'a', {
enumerable : true,
configurable : true,
get() {
return bValue
},
set(newVal) {
bValue = newVal
}
})
defineReactive
上述存取描述符的使用中,我們使用了一個(gè)全局變量bValue,去承載這個(gè)對(duì)象屬性值的變化,如果使用Object.defineProperty維護(hù)多個(gè)屬性,這種方式就會(huì)很冗余。于是就有了defineReactive(尤大牛逼),通過(guò)閉包作用域去維護(hù)這個(gè)變量,具體實(shí)現(xiàn)如下:
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