JavaScript原型——属性设置和屏蔽

给一个对象设置属性,这是对象的知识点,但是由于涉及到了原型链,就放在原型里了。

预备知识

Getter和Setter

getter和setter都是隐藏函数,getter会在获取属性值时调用,setter会在设置属性值时调用。
当你给一个属性定义getter、setter或者两者都有,那这个属性就被定义为"访问描述符"。
对于访问描述符,JavaScript会忽略它们的value和writable特性,只关心set和get(还有configurable、enumerable)特性。
var obj = {
    //给a定义一个getter
    get a() {
        return 1;
    }
}

Object.defineProperty(obj,"b",{
    //给b定义一个getter
    get: function() {
        return this.a*2;
    },
    enumerable: true
});

obj.a;  //1
obj.b;  //2

上面两种方式都会在对象中创建一个不包含值得属性,对这个属性的访问会自动调用一个隐藏函数,它的返回值会被当做属性访问的返回值:

var obj= {
    //给a定义一个getter
    get a() {
        return 1;
    }
};

obj.a = 2;
obj.a;  //1

只定义了a的getter,赋值是没有意义的。所以getter和setter应当一起出现:

var obj = {
    //给a定义一个getter
    get a() {
        return this._a;
    },

    //给a定义一个setter
    set a(val) {
        this._a = val*2;
    }
};

obj.a = 1;
obj.a;  //2

完整的赋值过程

obj.a = 1;

1、如果obj中已经有了a属性(若原型链上层也有a属性,就会发生屏蔽),该语句会修改已有的属性值(总会选择原型链最底层的a属性)。

其实赋值会触发一个[[Put]]方法,对obj中已经有了a属性,它大概会检查下面这些内容:
1、属性是否是访问描述符?如果是并且存在setter就调用setter。
2、属性的数据描述符中writable是否是false?如果是,在非严格模式下静默失败,在严格模式下TypeError异常。
3、如果都不是,将该值设置为属性的值。

2、如果obj中没有a属性,就会遍历原型链。如果原型链上也没有找到a属性,a就被添加到obj上。
3、如果obj中没有a属性,原型链上层有a属性,那么有三种情况:

  • 如果原型链上层存在名为a的普通数据访问,并且没有被标记为已读(即writable: true),则直接在obj中添加一个名为a的新属性(它是屏蔽属性)。
var anotherObj = {a:1};
var obj = Object.create(anotherObj);
obj.a = 2;
obj;    //{a: 2}
  • 如果在原型链上层存在a属性,但是被标记为只读(即writable: false),那么无法修改已有属性或在obj上创建屏蔽属性。如果运行在严格模式,会抛出一个错误;如果运行在非严格模式,这条语句会被忽略。
var anotherObj = {};
Object.defineProperty(anotherObj,"a",{
    value: 1,
    writable: false
});
var obj = Object.create(anotherObj);
obj.a = 2;  //该语句是没有效果的
obj;    //{}
obj.a;  //1

但是,也可以屏蔽a属性:

var anotherObj = {};
Object.defineProperty(anotherObj,"a",{
    value: 1,
    writable: false
});
var obj = Object.create(anotherObj);

Object.defineProperty(obj,"a",{
    value: 2
});

obj;    //{a: 2}

屏蔽a属性,不能使用=操作符来赋值,而是通过Object.defineProperty()向obj添加a属性。

  • 如果在原型链上层存在a属性,并且它是一个setter,那就会调用这个setter,a属性也不会被添加到obj,,也不会重新定义a这个setter。
var anotherObj = {
    get a() {
        return this._a;
    },

    set a(val) {
        this._a = val;
    }
};

var obj = Object.create(anotherObj)
obj.a = 2;

obj;    //{_a: 2}

同上,也可以通过Object.defineProperty()来屏蔽a属性:

var anotherObj = {
    get a() {
        return this._a;
    },

    set a(val) {
        this._a = val;
    }
};

var obj = Object.create(anotherObj);

Object.defineProperty(obj,"a",{
    value: 2
});

obj;    //{a: 2}

注意:有时候会产生隐式屏蔽:

var anotherObj = {a: 2};

var obj = Object.create(anotherObj);

anotherObj.a;   //2
obj.a;  //2

anotherObj.hasOwnProperty("a"); //true
obj.hasOwnProperty("a");    //false

obj.a++;

anotherObj.a;   //2
obj.a;  //3

obj.hasOwnProperty("a");    //true

修改委托属性时要小心,如果想要anotherObj.a的值增加,唯一的办法就是anotherObj.a++。

你可能感兴趣的:(js)