屏蔽属性
在写之前,先看一段代码:
function Foo(){} Foo.prototype.a=4; var instance=new Foo(); instance.a='lala';
大家猜,Foo.prototype对象上的a属性的值会不会被修改?我们可以来测试一下:
function Foo(){} Foo.prototype.a=4; var instance=new Foo(); instance.a='lala'; console.log(Foo.prototype.a); // 4 console.log(instance.a); // lala
显然答案是不会。实际上,会在实例instance本身创建一个同名的a属性:
function Foo(){} Foo.prototype.a=4; var instance=new Foo(); instance.a='lala'; console.log(instance.hasOwnProperty('a')); // true
这就是我们所说的“屏蔽属性”,当要输出instance.a的值时,按照原型链的查找规则,首先会在instance本身查找有没有a属性,没有的话会沿它的原型对象找,直到找到就停止查找,所以,这个例子要输出instance.a则会输出lala,而不会输出4,相当于屏蔽了原型对象上的同名属性。
但是会为instance添加a属性是有条件的,条件就是原型对象上的同名属性是可写的(writable:true),如果不可写(writable:false),则既不会修改原型上的a属性也不会为instance创建一个a属性:
function Foo(){} Foo.prototype.a=4; Object.defineProperty(Foo.prototype,'a',{ writable:false // 不可写 }); var instance=new Foo(); instance.a='lala'; console.log(instance.hasOwnProperty('a')); // false console.log(Foo.prototype.a); // 4
另一种情况是,如果原型对象上的同名属性是一个具有setter方法的属性,则执行instance.a='lala';调用的是setter方法而不是为instance添加a属性:
function Foo(){} Foo.prototype.a=4; Object.defineProperty(Foo.prototype,'a',{ set:function(){ console.log('lala'); } }); var instance=new Foo(); instance.a=100; // lala console.log(instance.hasOwnProperty('a')); // false console.log(Foo.prototype.a); // undefined
注意,setter方法是由instance对象调用的(注意this):
function Foo(){} Object.defineProperty(Foo.prototype,'a',{ set:function(){ this.age=20; } }); var instance=new Foo(); instance.a=100; console.log(instance.hasOwnProperty('age')); // true console.log(instance.a); // 20
你看,instance本身就有了age属性,也就是说,如果利用setter定义任何属性,这个操作只针对instance本身。