最近在学习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