对象属性的属性
引子
vue 的大行其道, 使得 Object.defineProperty 被更多人所認識. vue 利用 Object.defineProperty 提供的特性實現了數據綁定.
難道 Object.defineProperty 能做的只有這些么? 很顯然不是.
屬性描述符
The Property Descriptor type is used to explain the manipulation and reification of Object property attributes.
屬性描述符到底是什么? 說白了就是對象屬性的屬性解釋與具化, 就是對象屬性本身具有哪些屬性.
平時我們創建一個對象并為對象添加屬性時, 可以這樣
let obj = new Object() obj.a = 'hello world' 復制代碼有了對象字面量后, 想要達到同樣的效果就更加省事了. 現在也都提倡使用字面量來創建對象.
let obj = {a: 'hello world' } 復制代碼所以說程序員都是懶人嘛, 怎么簡單怎么來. 通過 Object.defineProperty 為對象添加屬性的方式也就淡出人們的視野.
同樣是為對象添加屬性, 它們有什么區別呢?
其實不論是通過賦值, 還是通過字面量, 還是通過 Object.defineProperty, 最終還是殊途同歸.
講真, 平時的開發中, 使用對象字面量創建對象并添加屬性時,壓根就不會考慮到對象屬性非個人意愿的改變了. 在我們看來, 對象就是存儲著鍵值、鍵值映射用的. 我們可以任意添加, 刪除, 更改對象屬性, 我們認為這是理所當然的. 現實也的確如此, 你有對它為所欲為的權利.
添加屬性的差異
可是為什么呢??
?你哪來的那么多為什么? 你為什么為什么呢!!!?
如果你稍微對 Object.defineProperty 有點了解, 應該知道通過這種方式定義的繁瑣. 你也應該知道對象屬性的操作也是有限制的. 想要放開權限, 我們需要這樣做
let obj = {} Object.defineProperty(obj, 'a', {value: 'hello world',configurable: true,enumerable: true,writable: true }) 復制代碼對象字面量添加屬性只是默認都為 true. 所以我們才可以為所欲為. 不相信么? 我們可以通過 Object.getOwnPropertyDescriptor 驗證一下
let obj = {} obj.a = 'hello world' let property = Object.getOwnPropertyDescriptor(obj, 'a') // > property // { value: 'hello world', // writable: true, // enumerable: true, // configurable: true } 復制代碼通過 Object.getOwnPropertyDescriptor 添加的屬性, 可以自由靈活地設置屬性描述符. 如果我不想配置的話, 它也有自己的默認值. 需要注意的是, 這里的默認值和對象字面量添加屬性與賦值屬性不同, 默認值為 false.
let obj = {} Object.defineProperty(obj, 'a', {value: 'hello world' }) let property = Object.getOwnPropertyDescriptor(obj, 'a') // > property // { value: 'hello world', // writable: false, // enumerable: false, // configurable: false } 復制代碼屬性描述符等位數據描述符和訪問描述符. 以上說的都是數據描述符.
傳聞中的Vue優化方案
道理我都懂, 那你這句話是啥個意思?
壓根就不會考慮到對象屬性非個人意愿的改變了.
在 Vue 的 data選項中,Vue 將遍歷此對象所有的屬性,并使用 Object.defineProperty 中的訪問描述符 get/set 訪問描述符重新定義一遍. 再結合觀察者模式, 每次屬性變化時都會收到通知, 從而達到數據綁定的效果. 顯然并不是所有的屬性都需要被轉換監聽.
對于展示型的數據, 就沒有必要也不會出現數據內部屬性的變化, 所以沒必要做以上的處理. Object.freeze (用法可見Object構造函數)處理后的對象, 就可以使得對象屬性添加、修改等操作失效. 這樣不去轉換也不用去監聽, 性能自然也就提高了.
屬性描述符鍵值
枯燥的描述開始...
數據描述符和訪問描述符同事具有的鍵值
configurable : 若為 false, 不能刪除該屬性, 不能切換屬性描述符(數據描述符切到訪問描述符,或訪問描述符切到數據描述符), 不能更改該屬性的屬性(對于數據描述符來說, value 屬性除外, Writable 屬性從 true 置為 false 除外). 說白了就是屬性描述符的開關, 管理著該屬性的屬性.
let obj = {} Object.defineProperty(obj, 'a', {value: 'hello world',configurable: false,enumerable: true,writable: true }) Object.defineProperty(obj, 'a', {value: 'hi world', }) let property_1 = Object.getOwnPropertyDescriptor(obj, 'a') Object.defineProperty(obj, 'a', {value: 'hi world',writable: false }) let property_2 = Object.getOwnPropertyDescriptor(obj, 'a') // > property_1 // { value: 'hi world', // writable: true, // enumerable: true, // configurable: false } // > property_2 // { value: 'hi world', // writable: false, // enumerable: true, // configurable: false }Object.defineProperty(obj, 'a', {value: 'hi world',configurable: false,enumerable: false }) // TypeError 復制代碼enumerable : 若為 true, 則在 for...in 枚舉中可被枚舉到.
let obj = {a: 'hello world',b: 'hi world' } Object.defineProperty(obj, 'c', {value: 'hey world',enumerable: false }) let propertyArray = Object.keys(obj) // > propertyArray // [ 'a', 'b' ] 復制代碼僅數據描述符具有的鍵值
value : 對象的該屬性對應的值. 可以為任何有效的 JavaScript 值.
writable : 若為false, 更改 value 將會失敗.
let obj = {} Object.defineProperty(obj, 'a', {value: 'hello world',writable: false,configurable: true }) obj.a = 'hi world' let property_1 = Object.getOwnPropertyDescriptor(obj, 'a') Object.defineProperty(obj, 'a', {value: 'hey world' }) let property_2 = Object.getOwnPropertyDescriptor(obj, 'a') // > property_1 // { value: 'hello world', // writable: false, // enumerable: false, // configurable: true } // > property_2 // { value: 'hey world', // writable: false, // enumerable: false, // configurable: true } 復制代碼從運行結果可以看出, 通過賦值更改屬性值時, 會更改無效.
但是通過 Object.defineProperty 更改時, 將會成功更改屬性值. 對此, 規范有提到, 當可配置不可寫時更改屬性值,會有 Writable 置為 true, => 設置 value => Writable 置為 false.
Step 8.b allows any field of Desc to be different from the corresponding field of current if current’s [[Configurable]] field is true. This even permits changing the [[Value]] of a property whose [[Writable]] attribute is false. This is allowed because a true [[Configurable]] attribute would permit an equivalent sequence of calls where [[Writable]] is first set to true, a new [[Value]] is set, and then [[Writable]] is set to false.
不過通過賦值和 Object.defineProperty 方式修改 value, 可以看出它們內部操作還是存在著差異.
僅訪問描述符具有的鍵值
get : 訪問該屬性時, 該方法都會被執行.
set : 修改該屬性時, 該方法都會被執行.
vue 就是利用了訪問描述符的這些特性, 實現了數據綁定.
let obj = {} let val Object.defineProperty(obj, 'a', {set(value) {val = valueconsole.log('set => value', val)},get() {console.log('get => value', val)return val},configurable: true,enumerable: true }) // > obj // { a: [Getter/Setter] } // > obj.a = 'hello world' // set => value hello world // 'hello world' // > obj.a // get => value hello world // 'hello world' 復制代碼over!
總結
- 上一篇: [BZOJ2716/2648][Viol
- 下一篇: socketserver