Object.defineProperty()的作用就是直接在一個物件上定義一個新屬性,或者修改一個已經存在的屬性。
Object.defineProperty(obj,prop,descriptor)
obj 要在其定義屬性的物件
prop 要定義或者修改的屬性的名稱
descriptor 將被定義或修改的屬性描述符
通過Object.defineProperty()為物件定義屬性,有兩種形式,且不能混合使用,分別為資料描述符,存取描述符。
下面分別描述兩者的區別:
資料描述符 -- 特有的兩個屬性(value,writable)
value 屬性值
writable 是否可以改變屬性的值。預設為false,不能改變。
let obj = {} Object.defineProperty(obj, 'name', { value: 'jack', writable: false // 不能改變屬性的值 }) obj.name = '123' console.log(obj)// {name: 'jack'}
let obj = {} Object.defineProperty(obj, 'name', { value: 'jack', writable: true // 可以改變屬性的值 }) obj.name = '123' console.log(obj)// {name: 123}
存取描述符 -- 是由一對 getter、setter 函式功能來描述的屬性
get:一個給屬性提供getter的方法,如果沒有getter則為undefined。該方法返回值被用作屬性值。預設為undefined。
set:一個給屬性提供setter的方法,如果沒有setter則為undefined。該方法將接受唯一引數,並將該引數的新值分配給該屬性。預設值為undefined。
let obj = {} let temp = null Object.defineProperty(obj, 'name', { get: function (val) { return temp // console.log("set方法被呼叫"+val); }, set: function (val) { temp = val console.log("get方法被呼叫" + val); } }) obj.name = 'chen' console.log(obj.name) //chen
資料描述符和存取描述都具有以下描述符:
configrable 描述屬性是否配置,以及可否刪除
enumerable 描述屬性是否會出現在for in 或者 Object.keys()的遍歷中
configrable
單獨設定:configurable: false 不能刪除屬性、不能重新定義屬性
單獨設定:configurable: true 能刪除屬性
同時設定:configurable:true 和 writable:false 可以修改屬性值
注意:通過賦值的形式,不可以修改,因為 writable為false
let obj = {} Object.defineProperty(obj,'name',{ value: 'chen', configurable: true, writable: false }) Object.defineProperty(obj,'name',{ value: 'rose' }) //通過屬性定義的形式可以修改name的屬性值 console.log(obj.name) //通過賦值的形式,不可以修改,因為 writable為false obj.name = 'JS' console.log(obj.name)// rose
同時設定:configurable:false 和 writable:true 可以修改屬性值
注意:通過賦值的形式,可以修改,因為 writable為true
let obj = {} Object.defineProperty(obj,'name',{ value: 'chen', configurable: false, writable: true }) Object.defineProperty(obj,'name',{ value: 'rose' }) //通過屬性定義的形式可以修改name的屬性值 console.log(obj.name) // rose //通過賦值的形式,可以修改,因為 writable為 true obj.name = 'JS' console.log(obj.name)// JS
configurable 總結:
configurable: false 時,不能刪除當前屬性,且不能重新配置當前屬性的描述符(可以把writable的狀態由true改為false,而無法由false改為true),但是在writable: true的情況下,可以改變value的值
configurable: true時,可以刪除當前屬性,可以配置當前屬性所有描述符。
enumerable 看如下程式碼
let obj = {} Object.defineProperty(obj,'name',{ value: 'chen', enumerable: false }) obj.gender = 'HTML' Object.defineProperty(obj,'age',{ value: '110', enumerable: true }) console.log(Object.keys(obj)) //['gender', 'age'] for (let i in obj) { console.log(i)// gender,age } console.log(obj.propertyIsEnumerable('name')) // false console.log(obj.propertyIsEnumerable('gender')) // true console.log(obj.propertyIsEnumerable('age')) // true
注意:下面程式碼的區別
let obj = {} obj.gender = 'HTML' // 等價於下面的程式碼 Object.defineProperty(obj,'gender',{ // 等於這裡 value: 'HTML', configurable: true, writable: true, enumerable:true }) Object.defineProperty(obj,'age',{ // 等價於下面的程式碼 value: '120' }) Object.defineProperty(obj,'age',{ // 等於這裡 value: '120', configurable: false, writable: false, enumerable: false })
不變性
1、物件常量
結合writable: false 和 configurable: false 就可以建立一個真正的常量屬性(不可修改,不可重新定義或者刪除)
let obj = {} Object.defineProperty(obj,'name',{ value: 'HTML', configurable: false, writable: false, }) delete obj.name //不可刪除 obj.name = 'JS' //不可以重新賦值 //通過賦值,可以新增新屬性 obj.gender = 'CSS' console.log(obj.gender) // CSS //不可重新定義 Object.defineProperty(obj,'name',{value: 'chen'})// 報錯: Cannot redefine property: name
2、禁止擴充套件
如果你想禁止一個物件新增新屬性並且保留已有屬性,就可以使用 Object.preventExtensions(...)
禁止擴充套件片段一:
'use strict' var obj = {name: 'JS'} Object.preventExtensions(obj) obj.gender = 'CSS' console.log(obj.gender) // Cannot add property gender, object is not extensible
禁止擴充套件片段二:
var obj = {name: 'JS'} Object.preventExtensions(obj) // 禁止擴充套件 // 但是仍然可以進行配置 Object.defineProperty(obj, 'name',{ value: 'HTML', writable: false, configurable: false }) console.log(obj.name) // HTML //不能進行擴充套件 obj.gender = 'CSS' console.log(obj.gender) //undefined
注意:在非嚴格模式下,建立屬性gender會靜默失敗,在嚴格模式下,將會丟擲異常。
3、密封
Object.seal()會建立一個密封的物件,這個方法實際上會在一個現有物件上呼叫object.preventExtensions(...)並把所有現有屬性標記為configurable:false。
var obj = { name: 'HTML' } Object.seal(obj) obj.gender = 'CSS' // 不能擴充套件屬性 console.log(obj.gender)// undefined // 再次驗證 console.log(Object.keys(obj)) // ['name'] // 不能再次配置屬性 Object.defineProperty(obj,'name',{ // Cannot redefine property: name name: 'JS', configurable: true })
密封之後不僅不能新增新屬性,也不能重新配置或者刪除任何現有屬性(雖然可以改屬性的值)
4、凍結
Object.freeze()會建立一個凍結物件,這個方法實際上會在一個現有物件上呼叫Object.seal(),並把所有現有屬性標記為writable: false,這樣就無法修改它們的值。
var obj = { name: 'HTML' } Object.freeze(obj) obj.name = 'Chen' // 不可以修改已有屬性的值 console.log(obj.name) // HTML
這個方法是可以應用在物件上級別最高的不可變性,它會禁止對於物件本身及其任意直接屬性的修改(但是這個物件引用的其他物件是不受影響的)
你可以深度凍結一個物件,具體方法為,首先這個物件上呼叫Object.freeze()然後遍歷它引用的所有物件,並在這些物件上呼叫Object.freeze()。
但是一定要小心,因為這麼做有可能會無意中凍結其他共享物件。
var obj = {
name: 'HTML'
}
Object.freeze(obj)
obj.name = 'Chen'
// 不可以修改已有屬性的值
console.log(obj.name) // HTML