詳解JavaScript之神奇的Object.defineProperty

摘要: JavaScript有個很神奇的Object.defineProperty(),瞭解一下?javascript

=與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並不同:java

// 示例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屬性不能賦值、不能遍歷並且不能刪除:ui

// 示例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中的代碼會報錯,下文可見。spa

writable

writable爲false時,屬性不能再次賦值,嚴格模式下會報錯**「Cannot assign to read only property」**(若是你但願實時監控相似的應用錯誤的話,歡迎免費試用Fundebug,咱們支持前端網頁、微信小程序、微信小遊戲,Node.js以及Java錯誤監控!):debug

// 示例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上測試。

參考

關於Fundebug

Fundebug專一於JavaScript、微信小程序、微信小遊戲,Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了5億+錯誤事件,獲得了衆多知名用戶的承認。歡迎免費試用!

相關文章
相關標籤/搜索