首页 > js中原型对象的writable为什么会影响到实例对象?

js中原型对象的writable为什么会影响到实例对象?

我定义了一个Person构造函数

function Person(){

}

然后定义了一个对象

var per1 =  new Person();

在Person原型对象中定义了一个属性like,并将可写性设置为false

Object.defineProperty(Person.prototype,"like",{
    value:"eating",
    enumerable:false,
    writable:false,
    configurable:true
});

修改per1的like值

per1的like属性不可更改,这是为什么?原型对象的writable为何会影响到实例对象上?
随后我又将原型对象的writable置为true

Object.defineProperty(Person.prototype,"like",{
    value:"eating",
    enumerable:false,
    writable:true,
    configurable:true
});

per1的like属性值可更改了,随后我再将原型对象的writable置为false

Object.defineProperty(Person.prototype,"like",{
    value:"eating",
    enumerable:false,
    writable:false,
    configurable:true
});

而per1的like属性又不再受到原型writable的影响,此时运行如下

那么原型对象的writable到底是如何实例对象的,为什么会产生如上效果


JS中属性的读取和设置是不同的
读取的时候按实例对象->原型对象->原型对象..的顺序查找,都找不到返回undefined
而赋值时,首先在实例对象中寻找是否有属性,
如果没有这个属性,那么就把这个属性添加到实例对象上;
但是有例外,如果其原型链中有这个属性,但其为只读,那么就不会实例对象上添加这个新的属性,赋值操作无效
如果其原型链中有这个属性,但其不为只读,那么就会在实例对象上添加这个新的属性

如果能再实例对象上找到这个属性,那么直接对此属性赋值(也不一定看其是否只读)

function Person(){
}
var per1 =  new Person();
Object.defineProperty(Person.prototype,"like",{
    value:"eating",
    enumerable:false,
    writable:false,
    configurable:true
});

like属性是定义在其原型上的并且其只读,那么
per1.like的任何修改都是无效的,也不会在当前实例对象上添加like属性

Object.defineProperty(Person.prototype,"like",{
    value:"eating",
    enumerable:false,
    writable:true,
    configurable:true
});

per1.like的可被修改,在当前实例对象上添加了属性like

per1.like=“gogog”;

for(var prop in per1){
 if(per1.hasOwnProperty(prop)){
   console.log(prop);//输出like
 }
}

此时你再

Object.defineProperty(Person.prototype,"like",{
    value:"eating",
    enumerable:false,
    writable:false,
    configurable:true
});

此时per1的实例上已经有了like属性,那就直接修改喽


由于你上面的代码片实在是太多了,我就按你的意思也一个类似的代码来解释吧
在定义实例上的属性时是不查找原型链的,但是有一个特殊的,也就是你说的第一种情况,当原型上的该属性的writable为false并且当前实例对象没有该属性时,由于该属性被规定好了是不可以写的,那么就不能更改这个值,所以就是不能再对实例对象定义该属性了,说点白话就是:实例对象一开始从原型上继承来了该属性,告诉了对象这个属性不可以写,所以在给对象定义时就出现了你说的第一种情况
下面俩种情况的代码:

    function Foo() {};
    var obj = new Foo();
    Object.defineProperty(Foo.prototype,"x",{
        value: "zp",
        writable: true
    });
    obj.x = "12345";
    console.log(obj.x);
    Object.defineProperty(Foo.prototype,"x",{
        value: "zp",
        writable: false
    });
    console.log(obj.x);
    obj.x = 20;
    console.log(obj.x);

简单来说,由于你一开始把原型上的writable属性写为了true,所以改变实例属性不查找原型链,下面你把原型上的该属性又改成了不可写,但是不影响实例对象的这个属性,因为这个属性是实例对象上的,它是可写的,所以可以更改


首先你要明白,原型上的方法和属性是被所有实例共同拥有的。
而构造函数上的方法和属性则是每个实例独自拥有。

当实例调用一个方法或者属性时,首先会查找构造函数是否具有该方法或者属性,如果没有,则会调用原型上的方法或者属性。

因此,实例如果调用的是原型的属性,那你修改了原型上like的writable,那访问肯定会受到影响嘛。

至于你试图第二次将like的writeable设置为false时,使用如下方法判断

Object.getOwnPropertyDescriptor(per1, 'like');

// 结果 Object {value: "bbb", writable: true, enumerable: true, configurable: true}

发现并没有修改成功。具体原因暂时未知。

【热门文章】
【热门文章】