1. 程式人生 > 實用技巧 >Object.defineProperty()詳解

Object.defineProperty()詳解

在園子裡看到一篇講解Object.defineProperty()的非常好的博文,我把他轉載過來,給自己留個筆記。原文出處https://www.cnblogs.com/junjun-001/p/11761252.html#commentform

  菜菜: “老大,那個, Object.defineProperty 是什麼鬼?”

  假設我們有個物件 user ; 我們要給它增加一個屬性 name , 我們會這麼做  

1 var user = {};
2 user.name="狂奔的蝸牛";
3 console.log(user);//{name: "狂奔的蝸牛"}

  如果想要增加一個sayHi方法叻?

  

1 user.sayHi=function () { console.log("Hi !") };
2 console.log(user);//{name: "狂奔的蝸牛", sayHi: ƒn}

  Object.defineProperty就是做這個的

  那麼Object.defineProperty 怎麼用?

  Object.defineProperty 需要三個引數(object , propName , descriptor)

  1 object 物件 => 給誰加
  2 propName 屬性名 => 要加的屬性的名字 【型別:String】
  3 descriptor 屬性描述 => 加的這個屬性有什麼樣的特性【型別:Object】

  那麼descriptor這個是個物件 ,他有那些屬性呢 ? 彆著急我們一個一個說;

  既然可以給一個物件增加屬性,那麼我們用它來做一下給 user新增 name屬性,程式碼是這樣的  

1 var user = {};
2 Object.defineProperty(user,"name",{
3  value:"狂奔的蝸牛"
4 })
5 console.log(user);//{name: "狂奔的蝸牛"}

  說明 是的還是那個經典的value屬性,他就是設定屬性值的。

  等等,屬性值只能為字串嗎?我們的 number function Object boolean 等呢?  

 1 var user = {};
 2 Object.defineProperty(user,"name",{
 3  value:"狂奔的蝸牛"
 4 })
 5 Object.defineProperty(user,"isSlow",{
 6  value:true
 7 })
 8 Object.defineProperty(user,"sayHi",{
 9  value:function () { console.log("Hi !") }
10 })
11 Object.defineProperty(user,"age",{
12  value:12
13 })
14 Object.defineProperty(user,"birth",{
15  value:{
16   date:"2018-06-29",
17   hour:"15:30"
18  }
19 })
20 console.log(user);

  

  說明 事實證明任何型別的資料都是可以的哦~

  問題又來了,如果 user物件已經有了name屬性,我們可以通過Object.defineProperty改變這個值嗎?

  我們來試試

1 var user = {};
2 Object.defineProperty(user,"name",{
3  value:"狂奔的蝸牛"
4 })
5 console.log(user);
6 user.name="新=>狂奔的蝸牛"
7 console.log(user);

  咦??為什麼我改了沒作用勒??

  原因:上邊說了descriptor有很多屬性,除了value屬性還有個 writable【顧名思義屬性是否可以被重新賦值】接受資料型別為 boolean(預設為false) true => 支援被重新賦值 false=>只讀

  哦哦,原來如果我沒設定writable值的時候就預設只讀啊,所以才改不掉

  那我們看看,設定為true,是不是就可以改掉了。

1 var user = {};
2 Object.defineProperty(user,"name",{
3  value:"狂奔的蝸牛",
4  writable:true
5 })
6 console.log(user);
7 user.name="新=>狂奔的蝸牛"
8 console.log(user);

  這個descriptor還有其他的屬性嗎?enumerable【顧名思義屬性是否可以被列舉】接受資料型別為 boolean(預設為false) true => 支援被列舉 false=>不支援

  額。。。列舉??什....什麼意思?

  假設我們想知道這個 user物件有哪些屬性我們一般會這麼做  

 1 var user ={
 2  name:"狂奔的蝸牛",
 3  age:25
 4 } ;
 5  
 6 //es6
 7 var keys=Object.keys(user)
 8 console.log(keys);// ['name','age']
 9 //es5
10 var keys=[];
11 for(key in user){
12  keys.push(key);
13 } 
14 console.log(keys);// ['name','age'] 

  如果我們使用 Object.的方式定義屬性會發生什麼呢?我們來看下輸出

 1 var user ={
 2  name:"狂奔的蝸牛",
 3  age:25
 4 } ;
 5 //定義一個性別 可以被列舉
 6 Object.defineProperty(user,"gender",{
 7  value:"男",
 8  enumerable:true
 9 })
10  
11 //定義一個出生日期 不可以被列舉
12 Object.defineProperty(user,"birth",{
13  value:"1956-05-03",
14  enumerable:false
15 })
16  
17 //es6
18 var keys=Object.keys(user)
19 console.log(keys);
20 // ["name", "age", "gender"]
21  
22 console.log(user);
23 // {name: "狂奔的蝸牛", age: 25, gender: "男", birth: "1956-05-03"}
24 console.log(user.birth);
25 // 1956-05-03 

  說明 很明顯,我們定義為 enumerable=falsebirth屬性並沒有被遍歷出來,遍歷 => 其實就是列舉(個人理解啦,不喜勿噴哦~)

  總結 enumerable屬性取值為布林型別 true | false預設值為false,為真屬性可以被列舉;反之則不能。此設定不影響屬性的呼叫和 檢視物件的值。

  configurable是接下來我們要講的一個屬性,這個屬性有兩個作用:

  1 屬性是否可以被刪除
  2 屬性的特性在第一次設定之後可否被重新定義特性

 1 var user ={
 2  name:"狂奔的蝸牛",
 3  age:25
 4 } ;
 5 //定義一個性別 不可以被刪除和重新定義特性
 6 Object.defineProperty(user,"gender",{
 7  value:"男",
 8  enumerable:true,
 9  configurable:false
10 })
11  
12 //刪除一下
13 delete user.gender;
14 console.log(user);//{name: "狂奔的蝸牛", age: 25, gender: "男"}
15  
16 //重新定義特性
17 Object.defineProperty(user,"gender",{
18  value:"男",
19  enumerable:true,
20  configurable:true
21 })
22 // Uncaught TypeError: Cannot redefine property: gender
23 //會報錯,如下圖

  

  設定為 true

 1 var user ={
 2  name:"狂奔的蝸牛",
 3  age:25
 4 } ;
 5 //定義一個性別 可以被刪除和重新定義特性
 6 Object.defineProperty(user,"gender",{
 7  value:"男",
 8  enumerable:true,
 9  configurable:true
10 })
11  
12 //刪除前
13 console.log(user);
14 // {name: "狂奔的蝸牛", age: 25, gender: "男"}
15  
16 //刪除一下
17 delete user.gender;
18 console.log(user);
19 // {name: "狂奔的蝸牛", age: 25}
20  
21 //重新定義特性
22 Object.defineProperty(user,"gender",{
23  value:"男",
24  enumerable:true,
25  configurable:false
26 })
27  
28 //刪除前
29 console.log(user);
30 // {name: "狂奔的蝸牛", age: 25, gender: "男"}
31 //刪除一下 刪除失敗
32 delete user.gender;
33 console.log(user);
34 // {name: "狂奔的蝸牛", age: 25, gender: "男"}

  總結configurable設定為 true 則該屬性可以被刪除和重新定義特性;反之屬性是不可以被刪除和重新定義特性的,預設值為false(Ps.除了可以給新定義的屬性設定特性,也可以給已有的屬性設定特性哈

  最後我們來說說,最重要的兩個屬性setget(即存取器描述:定義屬性如何被存取),這兩個屬性是做什麼用的呢?我們通過程式碼來看看

 1 var user ={
 2  name:"狂奔的蝸牛"
 3 } ;
 4 var count = 12;
 5 //定義一個age 獲取值時返回定義好的變數count
 6 Object.defineProperty(user,"age",{
 7  get:function(){
 8   return count;
 9  }
10 })
11 console.log(user.age);//12
12  
13 //如果我每次獲取的時候返回count+1呢
14 var user ={
15  name:"狂奔的蝸牛"
16 } ;
17 var count = 12;
18 //定義一個age 獲取值時返回定義好的變數count
19 Object.defineProperty(user,"age",{
20  get:function(){
21   return count+1;
22  }
23 })
24 console.log(user.age);//13

  接下來我不用解釋了吧,你想在獲取該屬性的時候對值做什麼隨你咯~

  來來來,我們看看 set,不多說上程式碼

 1 var user ={
 2  name:"狂奔的蝸牛"
 3 } ;
 4 var count = 12;
 5 //定義一個age 獲取值時返回定義好的變數count
 6 Object.defineProperty(user,"age",{
 7  get:function(){
 8   return count;
 9  },
10  set:function(newVal){
11   count=newVal;
12  }
13 })
14 console.log(user.age);//12
15 user.age=145;
16 console.log(user.age);//145
17 console.log(count);//145
18  
19 //等等,如果我想設定的時候是 自動加1呢?我設定145 實際上設定是146
20  
21 var user ={
22  name:"狂奔的蝸牛"
23 } ;
24 var count = 12;
25 //定義一個age 獲取值時返回定義好的變數count
26 Object.defineProperty(user,"age",{
27  get:function(){
28   return count;
29  },
30  set:function(newVal){
31   count=newVal+1;
32  }
33 })
34 console.log(user.age);//12
35 user.age=145;
36 console.log(user.age);//146
37 console.log(count);//146

  說明注意:當使用了getter或setter方法,不允許使用writable和value這兩個屬性(如果使用,會直接報錯滴)

  get是獲取值的時候的方法,型別為function,獲取值的時候會被呼叫,不設定時為undefined

  set是設定值的時候的方法,型別為function,設定值的時候會被呼叫,undefined

  get或set不是必須成對出現,任寫其一就可以

 1 var user ={
 2  name:"狂奔的蝸牛"
 3 } ;
 4 var count = 12;
 5 //定義一個age 獲取值時返回定義好的變數count
 6 Object.defineProperty(user,"age",{
 7  get:function(){
 8   console.log("這個人來獲取值了!!");
 9   return count;
10  },
11  set:function(newVal){
12   console.log("這個人來設定值了!!");
13   count=newVal+1;
14  }
15 })
16 console.log(user.age);//12
17 user.age=145;
18 console.log(user.age);//146

  

  【完結】

  Object.defineProperty方法直接在一個物件上定義一個新屬性,或者修改一個已經存在的屬性, 並返回這個物件

  • value: 設定屬性的值
  • writable: 值是否可以重寫。true | false
  • enumerable: 目標屬性是否可以被列舉。true | false
  • configurable: 目標屬性是否可以被刪除或是否可以再次修改特性 true | false
  • set: 目標屬性設定值的方法
  • get:目標屬性獲取值的方法
1 2 3 varuser = {}; user.name="狂奔的蝸牛"; console.log(user);//{name: "狂奔的蝸牛"}

如果想要增加一個sayHi方法叻?

1 2 user.sayHi=function() { console.log("Hi !") }; console.log(user);//{name: "狂奔的蝸牛", sayHi: ƒn}

Object.defineProperty就是做這個的

那麼Object.defineProperty 怎麼用?

Object.defineProperty 需要三個引數(object , propName , descriptor)

1 object 物件 => 給誰加
2 propName 屬性名 => 要加的屬性的名字 【型別:String】
3 descriptor 屬性描述 => 加的這個屬性有什麼樣的特性【型別:Object】

那麼descriptor這個是個物件 ,他有那些屬性呢 ? 彆著急我們一個一個說;

既然可以給一個物件增加屬性,那麼我們用它來做一下給 user新增 name屬性,程式碼是這樣的

1 2 3 4 5 varuser = {}; Object.defineProperty(user,"name",{ value:"狂奔的蝸牛" }) console.log(user);//{name: "狂奔的蝸牛"}

說明 是的還是那個經典的value屬性,他就是設定屬性值的。

等等,屬性值只能為字串嗎?我們的 number function Object boolean 等呢?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 varuser = {}; Object.defineProperty(user,"name",{ value:"狂奔的蝸牛" }) Object.defineProperty(user,"isSlow",{ value:true }) Object.defineProperty(user,"sayHi",{ value:function() { console.log("Hi !") } }) Object.defineProperty(user,"age",{ value:12 }) Object.defineProperty(user,"birth",{ value:{ date:"2018-06-29", hour:"15:30" } }) console.log(user);

說明 事實證明任何型別的資料都是可以的哦~

問題又來了,如果 user物件已經有了name屬性,我們可以通過Object.defineProperty改變這個值嗎?

我們來試試

1 2 3 4 5 6 7 varuser = {}; Object.defineProperty(user,"name",{ value:"狂奔的蝸牛" }) console.log(user); user.name="新=>狂奔的蝸牛" console.log(user);

咦??為什麼我改了沒作用勒??

原因:上邊說了descriptor有很多屬性,除了value屬性還有個 writable【顧名思義屬性是否可以被重新賦值】接受資料型別為 boolean(預設為false) true => 支援被重新賦值 false=>只讀

哦哦,原來如果我沒設定writable值的時候就預設只讀啊,所以才改不掉

那我們看看,設定為true,是不是就可以改掉了。

1 2 3 4 5 6 7 8 varuser = {}; Object.defineProperty(user,"name",{ value:"狂奔的蝸牛", writable:true }) console.log(user); user.name="新=>狂奔的蝸牛" console.log(user);

這個descriptor還有其他的屬性嗎?enumerable【顧名思義屬性是否可以被列舉】接受資料型別為 boolean(預設為false) true => 支援被列舉 false=>不支援

額。。。列舉??什....什麼意思?

假設我們想知道這個 user物件有哪些屬性我們一般會這麼做

1 2 3 4 5 6 7 8 9 10 11 12 13 14 varuser ={ name:"狂奔的蝸牛", age:25 } ; //es6 varkeys=Object.keys(user) console.log(keys);// ['name','age'] //es5 varkeys=[]; for(key inuser){ keys.push(key); } console.log(keys);// ['name','age']

如果我們使用 Object.的方式定義屬性會發生什麼呢?我們來看下輸出

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 varuser ={ name:"狂奔的蝸牛", age:25 } ; //定義一個性別 可以被列舉 Object.defineProperty(user,"gender",{ value:"男", enumerable:true }) //定義一個出生日期 不可以被列舉 Object.defineProperty(user,"birth",{ value:"1956-05-03", enumerable:false }) //es6 varkeys=Object.keys(user) console.log(keys); // ["name", "age", "gender"] console.log(user); // {name: "狂奔的蝸牛", age: 25, gender: "男", birth: "1956-05-03"} console.log(user.birth); // 1956-05-03

說明 很明顯,我們定義為 enumerable=falsebirth屬性並沒有被遍歷出來,遍歷 => 其實就是列舉(個人理解啦,不喜勿噴哦~)

總結 enumerable屬性取值為布林型別 true | false預設值為false,為真屬性可以被列舉;反之則不能。此設定不影響屬性的呼叫和 檢視物件的值。

configurable是接下來我們要講的一個屬性,這個屬性有兩個作用:

1 屬性是否可以被刪除
2 屬性的特性在第一次設定之後可否被重新定義特性

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 varuser ={ name:"狂奔的蝸牛", age:25 } ; //定義一個性別 不可以被刪除和重新定義特性 Object.defineProperty(user,"gender",{ value:"男", enumerable:true, configurable:false }) //刪除一下 deleteuser.gender; console.log(user);//{name: "狂奔的蝸牛", age: 25, gender: "男"} //重新定義特性 Object.defineProperty(user,"gender",{ value:"男", enumerable:true, configurable:true }) // Uncaught TypeError: Cannot redefine property: gender //會報錯,如下圖

設定為 true

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 varuser ={ name:"狂奔的蝸牛", age:25 } ; //定義一個性別 可以被刪除和重新定義特性 Object.defineProperty(user,"gender",{ value:"男", enumerable:true, configurable:true }) //刪除前 console.log(user); // {name: "狂奔的蝸牛", age: 25, gender: "男"} //刪除一下 deleteuser.gender; console.log(user); // {name: "狂奔的蝸牛", age: 25} //重新定義特性 Object.defineProperty(user,"gender",{ value:"男", enumerable:true, configurable:false }) //刪除前 console.log(user); // {name: "狂奔的蝸牛", age: 25, gender: "男"} //刪除一下 刪除失敗 deleteuser.gender; console.log(user); // {name: "狂奔的蝸牛", age: 25, gender: "男"}

總結configurable設定為 true 則該屬性可以被刪除和重新定義特性;反之屬性是不可以被刪除和重新定義特性的,預設值為false(Ps.除了可以給新定義的屬性設定特性,也可以給已有的屬性設定特性哈

最後我們來說說,最重要的兩個屬性setget(即存取器描述:定義屬性如何被存取),這兩個屬性是做什麼用的呢?我們通過程式碼來看看

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 varuser ={ name:"狂奔的蝸牛" } ; varcount = 12; //定義一個age 獲取值時返回定義好的變數count Object.defineProperty(user,"age",{ get:function(){ returncount; } }) console.log(user.age);//12 //如果我每次獲取的時候返回count+1呢 varuser ={ name:"狂奔的蝸牛" } ; varcount = 12; //定義一個age 獲取值時返回定義好的變數count Object.defineProperty(user,"age",{ get:function(){ returncount+1; } }) console.log(user.age);//13

接下來我不用解釋了吧,你想在獲取該屬性的時候對值做什麼隨你咯~

來來來,我們看看 set,不多說上程式碼

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 varuser ={ name:"狂奔的蝸牛" } ; varcount = 12; //定義一個age 獲取值時返回定義好的變數count Object.defineProperty(user,"age",{ get:function(){ returncount; }, set:function(newVal){ count=newVal; } }) console.log(user.age);//12 user.age=145; console.log(user.age);//145 console.log(count);//145 //等等,如果我想設定的時候是 自動加1呢?我設定145 實際上設定是146 varuser ={ name:"狂奔的蝸牛" } ; varcount = 12; //定義一個age 獲取值時返回定義好的變數count Object.defineProperty(user,"age",{ get:function(){ returncount; }, set:function(newVal){ count=newVal+1; } }) console.log(user.age);//12 user.age=145; console.log(user.age);//146 console.log(count);//146

說明注意:當使用了getter或setter方法,不允許使用writable和value這兩個屬性(如果使用,會直接報錯滴)

get是獲取值的時候的方法,型別為function,獲取值的時候會被呼叫,不設定時為undefined

set是設定值的時候的方法,型別為function,設定值的時候會被呼叫,undefined

get或set不是必須成對出現,任寫其一就可以

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 varuser ={ name:"狂奔的蝸牛" } ; varcount = 12; //定義一個age 獲取值時返回定義好的變數count Object.defineProperty(user,"age",{ get:function(){ console.log("這個人來獲取值了!!"); returncount; }, set:function(newVal){ console.log("這個人來設定值了!!"); count=newVal+1; } }) console.log(user.age);//12 user.age=145; console.log(user.age);//146

【完結】

Object.defineProperty方法直接在一個物件上定義一個新屬性,或者修改一個已經存在的屬性, 並返回這個物件

  • value: 設定屬性的值
  • writable: 值是否可以重寫。true | false
  • enumerable: 目標屬性是否可以被列舉。true | false
  • configurable: 目標屬性是否可以被刪除或是否可以再次修改特性 true | false
  • set: 目標屬性設定值的方法
  • get:目標屬性獲取值的方法