屏蔽属性

在写之前,先看一段代码:

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本身。

原文地址:https://www.cnblogs.com/linweinb/p/8597051.html