屬性描述符給js添加了巨大的可能性,要想搞懂前端3大框架原理,不懂不行啊。前端
此函數傳入兩個參數,一個是目標對象,一個是目標對象的屬性,返回的是一個該屬性的屬性描述符對象,就像這樣。vue
var obj = {test: 'test'} console.log(Object.getOwnPropertyDescriptor(obj, 'test')) // 輸出 { value: 'test', writable: true, enumerable: true, configurable: true } // 建議各位看官,本身動手試一下噢
能夠看到,若是是用字面量聲明的對象,默認 writable enumerable configurable 的值都是true安全
此函數傳入3葛參數,第一個是目標對象,第二個是要修改的目標對象的屬性(能夠是目標對象沒有的屬性),第三個是屬性描述符對象,app
Object.defineProperty(myObject, 'test', { writable: false }) myObject.a = 'test again' console.log(myObject)// {test: 'test'} ???
oh 個人again呢? 框架
在writable 爲false 的時候,js會忽略掉 該屬性的賦值行爲,我去太坑了,不行也不告訴我一聲。(若想他告訴你,請使用嚴格模式)函數
若是我 define 一個不存在的屬性的property呢?就像這樣測試
Object.defineProperty(myObject, 'test2', { value: 'testtest' }) console.log(Object.getOwnPropertyDescriptor(myObject, 'test2')) // 輸出 { value: 'testtest', writable: false, enumerable: false, configurable: false }
我去,定義一個不存在的屬性會建立一個只有value, 其餘描述符都爲false的屬性,這樣定義的屬性,其餘描述符默認爲false(超級安全)this
Object.defineProperty(myObject, 'test', { configurable: false, value: 'testconfigurable', enumerable: true, writable: false }) Object.defineProperty(myObject, 'test', { writable: true }) myObject.test = 'config writable after set the configurable flase'; console.log(Object.getOwnPropertyDescriptor(myObject, 'test')) console.log(myObject.test) // error: cant not redefine.....
噢 報錯了,咱們把configurable 修改的同時 把 wriable 也弄成false了,而後 再把writable 給弄回來ture 發現報錯了,原來 configurable 是不讓咱們修改 test 這個屬性的描述符了嗎?code
咱們再來試一下,先修改configurable 再動 writable。對象
Object.defineProperty(myObject, 'test', { configurable: false, value: 'testconfigurable', enumerable: true, writable: true }) Object.defineProperty(myObject, 'test', { writable: false }) myObject.test = 'config writable after set the configurable flase'; console.log(Object.getOwnPropertyDescriptor(myObject, 'test')) console.log(myObject.test) // testconfigurable
個人天啊,what happen 賦值失敗??? writable false 生效? 什麼狀況。。。 只許州官放火,不準百姓點燈啊(不知道是bug,仍是另有緣由,不過誰讓他是js,就是這麼神奇)
既然是不可修改屬性,那我直接移除了呢?就像這樣
var myObject = {test: 'test'} console.log(myObject) // test delete myObject.test console.log(myObject.test) // undefined // myObject.a = 'bbb' myObject.test = 'test'; Object.defineProperty(myObject, 'test', { configurable: false, value: 'testconfigurable', enumerable: true, writable: true }) console.log(myObject) // testconfigurable delete myObject.test console.log(myObject) // testconfigurable
能夠看到,你刪我不到~,你刪我不到~
照常
var myObject = {test: 'test'} Object.defineProperty(myObject, 'test', { configurable: false, value: 'testconfigurable', enumerable: false, writable: true }) console.log(myObject) // 輸出 { value: 'testconfigurable', writable: true, enumerable: false, configurable: false } {}
?? 個人屬性呢? 莫得了? 可是描述符在啊? value 也在啊
enumerable 是控制屬性是否能 照常顯示的描述符,若爲false 不單隻console.log 不出來 就連 forin 循環也不會遍歷到他的key值
好了,全部的屬性描述符的性質都已經介紹過了,咱們已經知道了他們是什麼了,可是這些描述符有什麼用呢?爲何須要這些描述符呢?
試想一下,在沒有屬性描述符的時候,如何解決對象這種引用類型的不可變?不可能。。。
上面咱們已經說過,當一個屬性被設置爲configurable 爲 false 的時候,該屬性不能被刪除,當咱們設置writable 爲false 的時候,該屬性不能被修改,嗯?,這不就是不可變的嗎,上文演示的是字符串,那咱們來測試一下對象吧
var myObject = {test: {}} Object.defineProperty(myObject, 'test', { configurable: false, enumerable: true, writable: false }) myObject.test.test = 'test' console.log(myObject)// {test: {test}: 'test'}
我去,不行啊,仍是寫進去了,難道要用遞歸把他所有的子屬性的子屬性都這樣定義一遍?
不怕,官方有新的API 來支持這一個功能
該函數接受一個對象,該對象變得不可擴展
var myObject = { a: 2 }; Object.preventExtensions( myObject ); myObject.b = 3; myObject.b; // undefined var anotherObj = {test: {}} Object.preventExtensions( anotherObj); anotherObj.test.test = 3; anotherObj.test.test; // 3
從這段代碼,能夠看到 preventExtensions 僅僅是支持 頂層對象的不可擴展,並不能限制子層對象的擴展。
Object.seal(myObjct) 會把myObject 的全部屬性的 configurable 都設置爲false(注意writable 仍是爲true的),而且都不可擴展。(可是子層引用屬性仍然能被修改)
Object.freeze(myObjct) 會把myObject 的全部屬性的 configurable 都設置爲false writable 爲false,而且都不可擴展。(可是子層引用屬性仍然能被修改),這是你能在js中得到的最不可變的對象了。
對象描述符,除了屬性描述符之外,還有一種訪問器描述符。
Object.defineProperty(myObject, 'test', { get () { console.log('獲取值') return 'testsetestest' } }) myObject.test = 222; console.log(myObject.test); // 輸出 獲取值 testsetestest
Object.defineProperty(myObject, 'test', { get () { console.log('獲取值') return this.test2 }, set (val) { console.log('設置值') this.test2 = val } }) myObject.test = 'ttttttt' console.log(myObject.test2)f // ttttttt
綜合上面兩段代碼來看,當咱們給該屬性設置了get,而後再給該屬性賦值時,js會執行get方法 而且 把賦的值給忽略掉,若是咱們要讓get 和 set 按照正常的思惟工做起來,就要藉助另一個屬性 來達到正常操做的目的。感受這有點兒像 vue 中 watch 和 computed 的思惟工做方式,說不定他們有很大的關係。
好了,今天就到這吧,對於對象更深刻的就是原型鏈了,這個東西要說的話就另起一篇吧,否則這文章就太長了撒。