Object.defineProperty()語法說明
Object.defineProperty()
的作用就是直接在一個對象上定義一個新屬性,或者修改一個已經存在的屬性
Object.defineProperty(obj, prop, desc)
obj 需要定義屬性的當前對象
prop 當前需要定義的屬性名
desc 屬性描述符
一般通過為對象的屬性賦值的情況下,對象的屬性可以修改也可以刪除,但是通過Object.defineProperty()定義屬性,通過描述符的設置可以進行更精准的控制對象屬性。
屬性描述符
通過Object.defineProperty()為對象定義屬性,有兩種形式,且不能混合使用,分別為數據描述符,存取描述符,下面分別描述下兩者的區別:value和 get 是同一個作用,只能同時用一個。 writable和set是同一個作用,用一個。
數據描述符 --特有的兩個屬性(value,writable)
let Person = {} Object.defineProperty(Person, 'name', { value: 'jack', writable: true // 是否可以改變 })
注意,如果描述符中的某些屬性被省略,會使用以下默認規則:
存取描述符 --是由一對 getter、setter 函數功能來描述的屬性
get
:一個給屬性提供
getter
的方法,如果沒有
getter
則為
undefined
。該方法返回值被用作屬性值。默認為
undefined
。
set
:一個給屬性提供
setter
的方法,如果沒有
setter
則為
undefined
。該方法將接受唯一參數,並將該參數的新值分配給該屬性。默認值為
undefined
。
let Person = {} let temp = null Object.defineProperty(Person, 'name', { get: function () { return temp }, set: function (val) { temp = val } }) Person.name = 'hy' console.log('Person.name') //hy
數據描述符和存取描述均具有以下描述符
configrable 描述屬性是否配置,以及可否刪除
enumerable 描述屬性是否會出現在for in 或者 Object.keys()的遍歷中
1 、configurable:false不能刪除屬性
let Person = {} Object.defineProperty(Person, 'name', { value:'hy', configurable:false }) delete Person.name console.log(Person)//{Person:'hy'}
2、configurable:false不能重新定義屬性
let Person = {} Object.defineProperty(Person, 'name', { value:'hy', configurable:false }) Object.defineProperty(Person, 'name', { value:'dfj', }) console.log(Person)//Cannot redefine property: name
3、可以通過屬性定義的形式修改name的值
let Person = {} Object.defineProperty(Person, 'name', { value:'hy', configurable:true, writable:false }) Object.defineProperty(Person, 'name', { value:'dfj', }) console.log(Person)//{name: "dfj"}
4、當writable為true時,無論configuttable是什么,都可用過屬性定義形式或直接賦值形式改變name值
let Person = {} Object.defineProperty(Person, 'name', { value:'hy', configurable:false, writable:true }) Object.defineProperty(Person, 'name', { value:'dfj', }) console.log(Person)//{name: "dfj"} Person.name = 'xh' console.log(Person)//{name: "xh"}
總結:
- configurable: false 時,不能刪除當前屬性,且不能重新配置當前屬性的描述符(有一個小小的意外:可以把writable的狀態由true改為false,但是無法由false改為true),但是在writable: true的情況下,可以改變value的值
- configurable: true時,可以刪除當前屬性,可以配置當前屬性所有描述符
enumerable 代碼片段分析
1、當enumerable為true時,對象中的屬性可枚舉,反之則不能
let Person = {} Object.defineProperty(Person, 'name', { value: 'hy', enumerable:true }) Object.defineProperty(Person, 'age', { value: '25', enumerable:false }) Object.defineProperty(Person, 'sex', { value: 'man', enumerable:true }) Person.hobby = 'coding' for(let i in Person){ console.log(i) //name 、 sex 、 hobby } console.log(Object.keys(Person)) //["name", "sex", "hobby"]
不變形
1、對象常量:結合writable: false 和 configurable: false 就可以創建一個真正的常量屬性(不可修改,不可重新定義或者刪除)let Person = {} Object.defineProperty(Person, 'name', { value: 'hy', writable:false, configurable:false }) delete Person.name Person.name = 'xh' Person.age = '14' //可以設置新的屬性值 console.log(Person)//{age: "14", name: "hy"}
2、禁止擴展:如果你想禁止一個對象添加新屬性並且保留已有屬性,就可以使用Object.preventExtensions(...)
'use strict' //非嚴格模式下或創建失敗,嚴格模式下會報錯 let Person = { name : 'xh'} Object.preventExtensions(Person) Object.defineProperty(Person, 'name', { value: 'hy', writable:false, configurable:false }) console.log(Person) Person.age = '14' console.log(Person)// Cannot add property age, object is not extensible
3、密封Object.seal()會創建一個密封的對象,這個方法實際上會在一個現有對象上調用object.preventExtensions(...)並把所有現有屬性標記為configurable:false。
'use strict' let Person = { name : 'xh'} Object.seal(Person) //這一步相當於調用了preventExtensions 禁止添加新的屬性,且將原有屬性configurable改為false 則不能配置,但可以修改 Object.defineProperty(Person, 'name', { value: 'hy', configurable:true }) console.log(Person)//Cannot redefine property: name
所以, 密封之后不僅不能添加新屬性,也不能重新配置或者刪除任何現有屬性(雖然可以改屬性的值)
'use strict' let Person = { name : 'xh'} Object.seal(Person) Person.name = 'dfj' //改屬性值(修改) console.log(Person) Object.defineProperty(Person, 'name', { value: 'hy', configurable:true }) console.log(Person)//Cannot redefine property: name
4、Object.freeze()會創建一個凍結對象,這個方法實際上會在一個現有對象上調用Object.seal(),並把所有現有屬性標記為writable: false,這樣就無法修改它們的值
'use strict' let Person = { name : 'xh'} Object.freeze(Person) //調用了seal導致不能配置刪除 又將writable改為false 導致不能修改 Person.name = 'dfj' //Cannot assign to read only property 'name
你可以深度凍結一個對象,具體方法為,首先這個對象上調用Object.freeze()然后遍歷它引用的所有對象,並在這些對象上調用Object.freeze()。但是一定要小心,因為這么做有可能會無意中凍結其他共享對象。
總結:
屬性定義,通過Object.defineProperty()形式
如果Obj沒有名為Prop的自身屬性的話:如果Obj是可擴展的話,則創建Prop這個自身屬性,否則拒絕
如果Obj已經有了名為Prop的自身屬性:則按照下面的步驟重新配置這個屬性
如果這個已有的屬性是不可配置的,則進行下面的操作會被拒絕
1: 將一個數據屬性轉換成訪問器屬性,反之變然 2: 改變`[[Configurable]]`或`[[Enumerable]]` 3: 改變[[Writable]]由false變為true 4: 在`[[Writable]]`為`false`時改變`[[Value]]` 5: 改變[[Get]]或[[Set]]
否則這個已有的屬性可以被重新配置
屬性賦值,通過obj.prop = ''prop"形式
1、如果在原型鏈上存在一個名為P
的只讀屬性(只讀的數據屬性或者沒有setter
的訪問器屬性),則拒絕
2、如果在原型鏈上存在一個名為P
的且擁有setter
的訪問器屬性,則調用這個setter
3、如果沒有名為P
的自身屬性,則如果這個對象是可擴展的,就創建一個新屬性,否則,如果這個對象是不可擴展的,則拒絕
4、如果已經存在一個可寫的名為P的自身屬性,則調用Object.defineProperty(),該操作只會更改P
屬性的值,其他的特性(比如可枚舉性)都不會改變
作用以及影響
屬性的定義操作和賦值操作各自有自己的作用和影響。
賦值可能會調用原型上的setter
,定義會創建一個自身屬性。
原型鏈中的同名只讀屬性可能會阻止賦值操作,但不會阻止定義操作。如果原型鏈中存在一個同名的只讀屬性,則無法通過賦值的方式在原對象上添加這個自身屬性,必須使用定義操作才可以。這項限制是在ECMAScript 5.1中引入的




賦值運算符不會改變原型鏈上的屬性
不能通過為
obj.foo
賦值來改變
proto.foo
的值。這種操作只會在
obj
上新建一個自身屬性

對象字面量中的屬性是通過定義操作添加的。

注意:Object.defineProperties()和Object.defineProperty()是不一樣的方法
Object.defineProperty()
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
語法:Object.defineProperty(obj, prop, descriptor)
參數:
obj(將要被添加屬性或修改屬性的對象)
prop(與第一個對象中要添加的屬性一一對應)
descriptor(將被定義或修改的屬性的描述符
)
Object.defineProperties() 方法直接在一個對象上定義新的屬性或修改現有屬性,並返回該對象。
語法:Object.defineProperties(obj, props)
參數:
obj(將要被添加屬性或修改屬性的對象)
prop(與第一個對象中要添加的屬性一一對應)
var obj = {}; Object.defineProperties(obj, { "property1": { value: true, writable: true }, "property2": { value: "Hello", writable: false } // 等等. }); alert(obj.property2) //彈出"Hello"
再次啰嗦一次,記住以下兩種形式的區別:

上面的代碼等同於:

另一方面:

上面的代碼等同於:
