Object的defineProperty和defineProperties這兩個方法在js中的重要性十分重要,主要功能就是用來定義或修改這些內部屬性,與之相對應的getOwnPropertyDescriptor和getOwnPropertyDescriptors就是獲取這行內部屬性的描述。html
下面文章我先介紹數據描述符和存取描述符的屬性表明的含義,而後簡單介紹以上四個方法的基本功能,這些若是瞭解可直接跳過,最後我會舉例擴展及說明各內部屬性在各類場景下產生的實際效果,那纔是這篇文章的核心內容。本文章關於概念性的描述仍是會盡可能使用《javaScript高級教程》、MDN網站等概念,保證準確和易於你們理解,講解部分則結合我的理解和舉例說明。vue
數據屬性有4個描述內部屬性的特性java
[[Configurable]]
表示可否經過delete刪除此屬性,可否修改屬性的特性,或可否修改把屬性修改成訪問器屬性,若是直接使用字面量定義對象,默認值爲true。segmentfault
[[Enumerable]]
表示該屬性是否可枚舉,便是否經過for-in循環或Object.keys()返回屬性,若是直接使用字面量定義對象,默認值爲true函數
[[Writable]]
可否修改屬性的值,若是直接使用字面量定義對象,默認值爲true網站
[[Value]]
該屬性對應的值,默認爲undefinedspa
訪問器屬性也有4個描述內部屬性的特性雙向綁定
[[Configurable]]
和數據屬性的[[Configurable]]同樣,表示可否經過delete刪除此屬性,可否修改屬性的特性,或可否修改把屬性修改成訪問器屬性,若是直接使用字面量定義對象,默認值爲truecode
[[Enumerable]]
和數據屬性的[[Configurable]]同樣,表示該屬性是否可枚舉,便是否經過for-in循環或Object.keys()返回屬性,若是直接使用字面量定義對象,默認值爲truehtm
[[GET]]
一個給屬性提供 getter 的方法(訪問對象屬性時調用的函數,返回值就是當前屬性的值),若是沒有 getter 則爲 undefined。該方法返回值被用做屬性值。默認爲 undefined
[[SET]]
一個給屬性提供 setter 的方法(給對象屬性設置值時調用的函數),若是沒有 setter 則爲 undefined。該方法將接受惟一參數,並將該參數的新值分配給該屬性。默認爲 undefined
功能: 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。若是不指定configurable, writable, enumerable ,則這些屬性默認值爲false,若是不指定value, get, set,則這些屬性默認值爲undefined
語法: Object.defineProperty(obj, prop, descriptor)
obj: 須要被操做的目標對象 prop: 目標對象須要定義或修改的屬性的名稱 descriptor: 將被定義或修改的屬性的描述符var obj = new Object(); Object.defineProperty(obj, 'name', { configurable: false, writable: true, enumerable: true, value: '張三' }) console.log(obj.name) //張三 複製代碼
功能: 方法直接在一個對象上定義一個或多個新的屬性或修改現有屬性,並返回該對象。
語法:Object.defineProperties(obj,props)
obj : 將要被添加屬性或修改屬性的對象。 props : 該對象的一個或多個鍵值對定義了將要爲對象添加或修改的屬性的具體配置var obj = new Object(); Object.defineProperties(obj, { name: { value: '張三', configurable: false, writable: true, enumerable: true }, age: { value: 18, configurable: true } }) console.log(obj.name, obj.age) // 張三, 18 複製代碼
功能: 該方法返回指定對象上的一個自有屬性對應的屬性描述(自有屬性指的是直接賦予該對象的屬性,不須要從原型鏈上進行查找的屬性)
語法: Object.getOwnPropertyDescriptor(obj, prop)
//obj: 須要查找的目標對象, prop: 目標對象內屬性名稱var person = { name: '張三', age: 18 } var desc = Object.getOwnPropertyDescriptor(person, 'name'); console.log(desc) 結果以下 // { // configurable: true, // enumerable: true, // writable: true, // value: "張三" // } 複製代碼
功能:所指定對象的全部自身屬性的描述符,若是沒有任何自身屬性,則返回空對象。
語法: Object.getOwnPropertyDescriptors(obj)
//obj: 須要查找的目標對象var person = { name: '張三', age: 18 } var desc = Object.getOwnPropertyDescriptors(person); console.log(desc) // age: { // value: 18, writable: true, enumerable: true, configurable: true} // name: { // value: "張三", writable: true, enumerable: true, configurable: true} // __proto__: Object 複製代碼
若是設置configurable屬性爲false,則不可以使用delete操做符(在嚴格模式下拋出錯誤),
修改全部內部屬性值會拋出錯誤
在對象中添加一個數據描述符屬性
var person = {}; Object.defineProperty(person, 'name', { configurable: false, value: 'John' }) ; delete person.name // 嚴格模式下拋出錯誤 console.log(person.name) // 'John' 沒有刪除 Object.defineProperty(person, 'name', { configurable: true //報錯 }); Object.defineProperty(person, 'name', { enumerable: 2 //報錯 }); Object.defineProperty(person, 'name', { writable: true //報錯 }); Object.defineProperty(person, 'name', { value: 2 //報錯 }); 複製代碼
注意:以上是·最開始定義屬性描述符·時,writabl默認爲false,纔會出現上述效果,若是writable定義爲true, 則能夠修改[[writable]]和[[value]]屬性值,修改另外兩個屬性值報錯:
var obj = {}; Object.defineProperty(obj, 'a', { configurable: false, writable: true, value: 1 }); Object.defineProperty(obj, 'a', { // configurable: true, //報錯 // enumerable: true, //報錯 writable: false, value: 2 }); var d = Object.getOwnPropertyDescriptor(obj, 'a') console.log(d); // { // value: 2, // writable: false, // } 複製代碼
在對象中添加存取描述符屬性
var obj = {}; var aValue; //若是不初始化變量, 不給下面的a屬性設置值,直接讀取會報錯aValue is not defined var b; Object.defineProperty(obj, 'a', { configurable : true, enumerable : true, get: function() { return aValue }, set: function(newValue) { aValue = newValue; b = newValue + 1 } }) console.log(b) // undefined console.log(obj.a) // undefined, 當讀取屬性值時,調用get方法,返回undefined obj.a = 2; // 當設置屬性值時,調用set方法,aValue爲2 console.log(obj.a) // 2 讀取屬性值,調用get方法,此時aValue爲2 console.log(b) // 3 再給obj.a賦值時,執行set方法,b的值被修改成2,額外說一句,vue中的計算屬性就是利用setter來實現的 複製代碼
注意:
- getter和setter能夠不一樣時使用,但在嚴格模式下只使用一個,會拋出錯誤。
- 數據描述符與存取描述符不能夠混用,不然會拋出錯誤。
- 使用 var定義的任何變量,其
configurable
屬性值都爲false
,定義對象也是同樣.
當Writable爲false(而且configurable爲true),[value]能夠經過 defineProperty修改,但不能直接賦值修改。
var obj = {}; Object.defineProperty(obj, 'a', { configurable: true, enumerable: false, writable: false, value: 1 }); Object.defineProperty(obj, 'a', { configurable: false, enumerable: true, writable: false , value: 2 }); var d = Object.getOwnPropertyDescriptor(obj, 'a') console.log(d); // 結果以下 // { // value: 2, // writable: false, // enumerable: true, // configurable: false // } 可是若是直接複製修改 var obj = {} Object.defineProperty(obj, 'a', { configurable: true, enumerable: false, writable: false, value: 1 }); obj.a=2; var d = Object.getOwnPropertyDescriptor(obj, 'a') console.log(d); // 結果以下 // { // value: 1, // 沒有作出修改 // writable: false, // enumerable: true, // configurable: false // } 複製代碼
直接上例子
var obj = {}; Object.defineProperties(obj, { a: { value: 1, enumerable: false }, b: { value: 2, enumerable: true }, c: { value: 3, enumerable: false } }) obj.d = 4; //等同於 //Object.defineProperty(obj, 'd', { // configurable: true, // enumerable: true, // writable: true, // value: 4 //}) for(var key in obj) { console.log(key); // 打印一次b, 一次d, a和c屬性enumerable爲false,不可被枚舉 } var arr = Object.keys(obj); console.log(arr); // ['b', 'd'] 複製代碼
簡易的數據雙向綁定
//html <body> <p> input1=> <input type="text" id="input1"> </p> <p> input2=> <input type="text" id="input2"> </p> <div> 我每次比input1的值加1=> <span id="span"></span> </div> </body> //js var oInput1 = document.getElementById('input1'); var oInput2 = document.getElementById('input2'); var oSpan = document.getElementById('span'); var obj = {}; Object.defineProperties(obj, { val1: { configurable: true, get: function() { oInput1.value = 0; oInput2.value = 0; oSpan.innerHTML = 0; return 0 }, set: function(newValue) { oInput2.value = newValue; oSpan.innerHTML = Number(newValue) ? Number(newValue) : 0 } }, val2: { configurable: true, get: function() { oInput1.value = 0; oInput2.value = 0; oSpan.innerHTML = 0; return 0 }, set: function(newValue) { oInput1.value = newValue; oSpan.innerHTML = Number(newValue)+1; } } }) oInput1.value = obj.val1; oInput1.addEventListener('keyup', function() { obj.val1 = oInput1.value; }, false) oInput2.addEventListener('keyup', function() { obj.val2 = oInput2.value; }, false) 複製代碼