摘要: JavaScript有個很神奇的Object.defineProperty(),瞭解一下?
=與Object.defineProperty
為JavaScript物件新增或者修改屬性,有兩種不同方式:直接使用=賦值或者使用Object.defineProperty()定義。如下:
// 示例1
var obj = {};
// 直接使用=賦值
obj.a = 1;
// 使用Object.defineProperty定義
Object.defineProperty(obj, "b",
{
value: 2
});
console.log(obj) // 列印"{a: 1, b: 2}"
複製程式碼
這樣看兩者似乎沒有區別,對吧?但是,如果使用Object.getOwnPropertyDescriptor()檢視obj.a與obj.b的屬性的**描述描述符(property descriptor)**時,會發現=與Object.defineProperty並不一樣:
// 示例2
var obj = {};
obj.a = 1;
Object.defineProperty(obj, "b",
{
value: 2
});
console.log(Object.getOwnPropertyDescriptor(obj, "a")); // 列印"{value: 1, writable: true, enumerable: true, configurable: true}"
console.log(Object.getOwnPropertyDescriptor(obj, "b")); // 列印"{value: 2, writable: false, enumerable: false, configurable: false}"
複製程式碼
可知,使用=賦值時,屬性的屬性描述符value是可以修改的,而writable、enumerable和configurable都為true。
而使用Object.defineProperty()定義的屬性的屬性描述符writable、enumerable和configurable預設值為false,但是都可以修改。對於writable、enumerable和configurable的含義,從名字就不難猜中,後文也會詳細介紹。
使用=賦值,等價於使用Object.defineProperty()定義時,同時將writable、enumerable和configurable設為true。程式碼示例3和4是等價的:
// 示例3
var obj = {};
obj.name = "Fundebug";
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 列印{value: "Fundebug", writable: true, enumerable: true, configurable: true}
複製程式碼
// 示例4
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug",
writable: true,
enumerable: true,
configurable: true
});
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 列印{value: "Fundebug", writable: true, enumerable: true, configurable: true}
複製程式碼
Object.defineProperty()
使用Object.defineProperty()定義時若只定義value,則writable、enumerable和configurable預設值為false。程式碼示例5和6是等價的:
// 示例5
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug"
});
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 列印{value: "Fundebug", writable: false, enumerable: false, configurable: false}
複製程式碼
// 示例6
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug",
writable: false,
enumerable: false,
configurable: false
});
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // 列印{value: "Fundebug", writable: false, enumerable: false, configurable: false}
複製程式碼
由於writable、enumerable和configurable都是false,導致obj.name屬性不能賦值、不能遍歷而且不能刪除:
// 示例7
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug"
});
// writable為false,無法賦值
obj.name = "雲麒";
console.log(obj.name); // 列印"Fundebug"
// enumerable為false,無法遍歷
console.log(Object.keys(obj)); // 列印"[]"
// configurable為false,無法刪除
delete obj.name;
console.log(obj.name); // 列印"Fundebug"
複製程式碼
若在嚴格模式("use strict")下,示例7中的程式碼會報錯,下文可見。
writable
writable為false時,屬性不能再次賦值,嚴格模式下會報錯**“Cannot assign to read only property”**(如果你希望實時監控類似的應用錯誤的話,歡迎免費試用Fundebug,我們支援前端網頁、微信小程式、微信小遊戲,Node.js以及Java錯誤監控!):
// 示例8
"use strict"
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug",
writable: false,
enumerable: true,
configurable: true
});
obj.name = "雲麒"; // 報錯“Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'”
複製程式碼
writable為true時,屬性可以賦值,這一點讀者不妨自行測試。
enumerable
enumerable為false時,屬性不能遍歷:
// 示例9
"use strict"
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug",
writable: true,
enumerable: false,
configurable: true
});
console.log(Object.keys(obj)) // 列印"[]"
複製程式碼
enumerable為true時,屬性可以遍歷,這一點讀者不妨自行測試。
configurable
enumerable為false時,屬性不能刪除,嚴格模式下會報錯**“Cannot delete property”**:
// 示例10
"use strict"
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug",
writable: true,
enumerable: true,
configurable: false
});
delete obj.name // 報錯“Uncaught TypeError: Cannot delete property 'name' of #<Object>”
複製程式碼
enumerable為true時,屬性可以刪除,這一點讀者不妨自行測試。
writable與configurable
當writable與enumerable同時為false時,屬性不能重新使用Object.defineProperty()定義,嚴格模式下會報錯**“Cannot redefine property”**:
// 示例11
"use strict"
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug",
writable: false,
configurable: false
})
Object.defineProperty(obj, "name",
{
value: "雲麒"
}) // 報錯“Uncaught TypeError: Cannot redefine property: name”
複製程式碼
當writable或者enumerable為true時,屬性可以重新使用Object.defineProperty()定義,這一點讀者不妨自行測試。
本文所有程式碼示例都在Chrome 67上測試。
參考
- Object.defineProperty()
- Object.getOwnPropertyDescriptor()
- StackOverflow: Why can't I redefine a property in a Javascript object?
關於Fundebug
Fundebug專注於JavaScript、微信小程式、微信小遊戲,Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了5億+錯誤事件,得到了眾多知名使用者的認可。歡迎免費試用!