Object.defineProperty
可能大家不是很常用到,但其實它無所不在,甚至於許多 mvvm, mvc 的資料繫結都是透過此接口達成的.
像是以下這段簡單的 code
var person = { name: 'Camel' }; Object.getOwnPropertyDescriptor(person, 'name'); // {value: "Camel", writable: true, enumerable: true, configurable: true}
其實就等價於
var person = {}; Object.defineProperty(person, 'name', { value: 'Camel', configurable: true, writable: true, enumerable: true }); Object.getOwnPropertyDescriptor(person, 'name'); // {value: "Camel", writable: true, enumerable: true, configurable: true}
可以發現 object.defineProperty
有幾個可設定的 descriptor
接下來本文將針對這幾個設定的使用及應用場景做個說明
configurable: 預設為 false
已定義的 descriptor 是否可被更改 (writable:true
除外),以及 property 是否可被 delete
常見的例子是設定為 configurable:false
避免設定的 property 被 delete
e.g.
var person = {}; Object.defineProperty(person, 'name', { value: 'Camel', configurable: false, }); delete person.name // false console.log(person.name); // Camel
configurable:false
的例子其實蠻常見的,像是 browser 內建的 location
Object.getOwnPropertyDescriptor(window, 'location'); // {value: Location, writable: true, enumerable: true, configurable: false}
writable: 預設為 false
常見的是與 configurable:false
搭配使用,可以避免 descriptor
與value
被做任何的修改.(進階一點的方法可以用待會介紹的 getter)
var person = {}; Object.defineProperty(person, 'name', { value: 'Camel', configurable: false, writable: false }); person.name = 'Penny'; console.log(person.name); // Camel
enumable: 預設為 false
此 property 是否可以被列舉(for… in/Object.keys)
常用作於類似 prototype chain 的方式使用,用來設置一個 property 來存不希望被列舉的值.
var person = {}; Object.defineProperty(person, 'name', { value: 'Camel', enumerable: false }); console.log(Object.keys(person)); // [] console.log(Object.getOwnPropertyNames(person)) // ["name"]
除了以上介紹的三個 configurable
, writable
, enumable
,其實另外有兩個非常重要的 get
, set
能幫助你做到更多的事,繼續看看下面的例子吧 ~
get: 預設為 undefined
當存取此變數時會呼叫的 function,回傳值會作為存取此 property 的值.
直覺想到的簡單應用可以用做資料格式化
e.g.
var person = { firstName: 'Camel', lastName: 'Chang' }; Object.defineProperty(person, 'fullName', { get() { return this.firstName + ' ' + this.lastName; } }); console.log(person.fullName); // Camel Chang
set: 預設為 undefined
當設定值時會呼叫的 function,呼叫時會帶要設定的值為參數
應該蠻容易想到 set
相當適合用來做資料的 validation
e.g.
var person = {}; Object.defineProperty(person, 'discount', { get() { return this._discount || 95; }, set(value) { return this._discount = value >= 80 ? value : 80; } }); console.log(person.discount); // 95 person.discount = 75; console.log(person.discount); // 80
其實仔細想想,你會發現 getter
, setter
可以用在製作簡單的雙向資料繫結
像是 get
取值時先看看 dom 節點的狀態(view)決定值(model)
或是 set
設值前根據值(model)改變 dom 節點(view)
這篇就不細說太多複雜的實作,僅做簡單的概念與範例介紹.
順帶一提,其實 Object.defineProperty
所能提供的接口較為單純,你可能比較難區分是做 add/update/delete 的操作,當初在 Chrome 36 時有提出一個過渡的接口 Object.observe
,但後來僅有 Chrome 實作也就漸漸的 deprecated.
取而代之的是在 ES6 有另一個 Proxy 代理物件,可以讓你介接更多原生的接口,有機會的話大家可以先看看,或許之後會為 Proxy 寫篇文章 XD
參考資料:
https://stackoverflow.com/questions/22658488/object-getownpropertynames-vs-object-keys
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/observe
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Leave a Reply