? ? 最近在學習vuejs,了解到內部實現使用到了es5的Getters和Setters。之前看高程的時候,沒有重視這塊,今天查看一下文檔,了解了他們的作用,再次記錄一下,可供以后查看和共享。
定義Getters和Setters:(一共有三種定義方法)
第一種方法: 使用對象字面量創建對象時,可以像下面定義set 和set一樣定義,
1 var obj = { 2 "a": 1, 3 get b(){ 4 return this.a + 1; 5 }, 6 set c(val){ 7 this.a = val + this.a; 8 } 9 }; 10 11 console.log(obj.a); //初始值為1 12 console.log(obj.b); //2 13 obj.c = 3; 14 console.log(obj.a); //4
第二種方法:通過Object.defineProperty(...)來定義
1 var d = Date.prototype; 2 3 Object.defineProperty(d, "year", { 4 get : function() { 5 return this.getFullYear(); 6 }, 7 set: function(val) { 8 this.setFullYear(val); 9 } 10 }); 11 12 var date = new Date(); 13 console.log("*********"); 14 console.log(date.year); //2016 15 console.log("*********");
第三種方式:
1 function Filed(val){ 2 var value = val; 3 this.getValue = function(){ 4 return value; 5 }; 6 this.setValue = function(val){ 7 value = value = val; 8 }; 9 } 10 11 var f = new Filed(20); 12 console.log("\n**********************"); 13 console.log(f.getValue()); //20 14 f.setValue(30); 15 console.log(f.getValue()); //30
Getters和Setters可以做什么?
目的:給js語言增加覆蓋對象單個屬性默認的[[Get]]和[[Put]]能力。
實際上,Getters和Setters都是對象上的屬性,通過Getters可以調用對象上隱藏的函數來獲取值,通過Setters調用隱藏的函數,來為對象屬性賦值。
我們知道對象的屬性描述符有value、writable、configurable和enumerable。Getters和Setter也可被理解為對象的存取描述符。如果一個對象定義了Getter和Setter,value和writable會被忽略,js只考慮Getter、Setter、configurable和enumerable。
1 var obj = { 2 get a(){ 3 return 2; 4 } 5 }; 6 7 Object.defineProperty(obj, "b", { 8 get: function(){ 9 return this.a * 4; 10 }, 11 enumerable: true 12 }); 13 14 console.log(obj.a); //2 15 console.log(obj.b); //8 16 obj.a = 4; 17 console.log(obj.a); //2
在16行的時候,我們對a進行賦值,但是在17行的時候,依舊輸出了2,,這是因為一旦存在存在存取描述符,并且僅設有Getter的話(即便設有Setter),對該屬性的賦值會被默認忽略。
1 var obj = { 2 get a(){ 3 return 2; 4 }, 5 set c(val){ 6 this.a = 3; 7 } 8 }; 9 10 Object.defineProperty(obj, "b", { 11 get: function(){ 12 return this.a * 4; 13 }, 14 enumerable: true 15 }); 16 17 console.log(obj.a); //2 18 console.log(obj.b); //8 19 //obj.a = 4; 20 obj.c = 4; 21 console.log(obj.a); //2
實際使用代碼如下:
1 var myObject = { 2 // define a getter for `a` 3 get a() { 4 return this._a_; 5 }, 6 7 // define a setter for `a` 8 set a(val) { 9 this._a_ = val * 2; 10 } 11 }; 12 13 myObject.a = 2; 14 15 console.log(myObject.a); // 4
通過Setter和Getter,我們可以動態設置和獲取對象屬性的值得效果:
1 var expr = "foo"; 2 var obj = { 3 a: "aa", 4 set [expr](val){ 5 this.a = val; 6 }, 7 get [expr](){ 8 return expr 9 } 10 }; 11 12 obj.foo="bb"; 13 console.log(obj.a); //bb 14 console.log(obj[expr]); //foo
對于上面的動態設置和獲取,不曉得實際用途。
js的[[Get]]和[[Put]]操作
Getter和Setter會對js對象屬性的存取產生怎樣的影響呢?
[[Get]]操作:
1 var obj = { 2 a: 2 3 }; 4 console.log(obj.a); //2
上面代碼的背后的機制:
執行的時候,JavaScript 會在對象 obj上執行一次 [[Get]] 操作。JavaScript 內置的 [[Get]] 操作首先根據屬性名稱檢查對象本身是否有該屬性,如果這時候找到了,那么就返回它的值;如果沒找到,那么會通過該對象的Prototype
?鏈繼續向上查找,直到頂層的?Object.prototype
。見下面代碼:
1 var OldObj = function(){}; 2 OldObj.prototype.a = 2; 3 var obj = new OldObj(); 4 console.log(obj.a); //2
[[put]]操作:
1 var obj = { 2 a: 2 3 }; 4 obj.a = 3;
背后的機制:
首先,JavaScript 會觸發一個 [[Put]] 操作,它的行為會根據要賦值的屬性是否已經直接存在于該對象上而有所不同。如果該屬性已經存在了,[[Put]] 操作會執行如下步驟:
- 該屬性是否已經定義了?
Setter
,如果已經定義了,那么調用它; - 該屬性的屬性描述符 (Property Descriptor)是否定義了?
writable: false
,如果是,那么賦值操作被忽略,如果是?strict mode
,那么會拋出?TypeError
?異常; - 如果沒有上述情況,那么給已存在的屬性賦值。
如果被賦值的屬性不是直接存在于對象上:
- [[Put]] 操作首先會搜索?
Prototype
?鏈,如果沒有找到?foo
,那么就在被賦值的對象上直接創建?foo
,并賦值為?bar
; - 如果在?
Prototype
?鏈上找到了?foo
,代碼的行為就會變得詭異起來,我們回頭再看。在此之前,先來了解一個概念,叫做變量隱藏 (Variable Shadowing)。當一個變量既直接存在于對象本身,又存在于對象的?Prototype
?鏈,那我們就可以說對象本身的屬性?foo
?隱藏了存在于?Prototype
?鏈的變量?foo
。理解了這個概念,我們再來看當變量?foo
?在?Prototype
?鏈上存在的的時候,給?foo
?賦值可能帶來的三種結果:- 如果?
foo
?在?Prototype
?鏈上存在,并且它沒有被定義為只讀的 (writable: false),那么一個名叫?foo
?的屬性會被直接添加到?myObject
,從而造成?變量隱藏 (Shadowing)
; - 如果?
foo
?在?Prototype
?鏈上存在,并且被標記為只讀的 (writable: false),那么對?foo
?的賦值操作會被忽略;如果是?strict mode
,那么會拋出異常; - 如果?
foo
?在?Prototype
?鏈上存在,并且它具有?Setter
,那么?Setter
?始終會被調用,也就是說沒有屬性會被直接添加到?myObject
?上,也不會發生變量隱藏。也許你還記得,當對象屬性?Setter
?存在的時候,給該屬性賦值,不會檢查?writable
。
- 如果?
原文地址:js Getters和Setters