★JavaScript中的Object.defineProperty()和defineProperties()

Object的defineProperty和defineProperties這兩個方法在js中的重要性十分重要,主要功能就是用來定義或修改這些內部屬性,與之相對應的getOwnPropertyDescriptor和getOwnPropertyDescriptors就是獲取這行內部屬性的描述。

下面文章我先介紹數據描述符和存取描述符的屬性代表的含義,然後簡單介紹以上四個方法的基本功能,這些如果瞭解可直接跳過,最後我會舉例擴展及說明各內部屬性在各種場景下產生的實際效果,那纔是這篇文章的核心內容。本文章關於概念性的描述還是會盡量使用《javaScript高級教程》、MDN網站等概念,保證準確和易於大家理解,講解部分則結合個人理解和舉例說明。


數據(數據描述符)屬性

數據屬性有4個描述內部屬性的特性

[[Configurable]]

表示能否通過delete刪除此屬性,能否修改屬性的特性,或能否修改把屬性修改爲訪問器屬性,如果直接使用字面量定義對象,默認值爲true。

[[Enumerable]]

表示該屬性是否可枚舉,即是否通過for-in循環或Object.keys()返回屬性,如果直接使用字面量定義對象,默認值爲true

[[Writable]]

能否修改屬性的值,如果直接使用字面量定義對象,默認值爲true

[[Value]]

該屬性對應的值,默認爲undefined


訪問器(存取描述符)屬性

訪問器屬性也有4個描述內部屬性的特性

[[Configurable]]

和數據屬性的[[Configurable]]一樣,表示能否通過delete刪除此屬性,能否修改屬性的特性,或能否修改把屬性修改爲訪問器屬性,如果直接使用字面量定義對象,默認值爲true

[[Enumerable]]

和數據屬性的[[Configurable]]一樣,表示該屬性是否可枚舉,即是否通過for-in循環或Object.keys()返回屬性,如果直接使用字面量定義對象,默認值爲true

[[GET]]

一個給屬性提供 getter 的方法(訪問對象屬性時調用的函數,返回值就是當前屬性的值),如果沒有 getter 則爲 undefined。該方法返回值被用作屬性值。默認爲 undefined

[[SET]]

一個給屬性提供 setter 的方法(給對象屬性設置值時調用的函數),如果沒有 setter 則爲 undefined。該方法將接受唯一參數,並將該參數的新值分配給該屬性。默認爲 undefined


創建/修改/獲取屬性的方法


YI、Object.defineProperty()

功能:
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。如果不指定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)  //張三
ER、Object.defineProperties()

功能:
方法直接在一個對象上定義一個或多個新的屬性或修改現有屬性,並返回該對象。
語法: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
SAN、Object.getOwnPropertyDescriptor()

功能:
該方法返回指定對象上的一個自有屬性對應的屬性描述(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進行查找的屬性)
語法: 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: "張三"
 // }

SI、Object. getOwnPropertyDescriptors()

功能:所指定對象的所有自身屬性的描述符,如果沒有任何自身屬性,則返回空對象。
語法: 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

如果設置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

當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
// }
Enumerable

直接上例子

 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']
get 和 set

簡易的數據雙向綁定

 //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)
原文鏈接地址:https://segmentfault.com/a/1190000011294519?utm_source=tag-newest#articleHeader15

如有疑問請添加我的微信號:18231133236。歡迎交流!
更多內容,請訪問的我的個人博客:https://www.liugezhou.online.
您也可以關注我的個人公衆號:【Dangerous Wakaka】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章