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](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/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