1

Assignment statements like me.name = 'forceddd' can be seen everywhere in JavaScript. But, do we really understand what this code does? Did you create a new attribute? Did you modify the value of the original attribute? Did this operation succeed or fail? This is just a simple assignment statement, but if we think about it carefully, we will find that this kind of detail is actually not that simple.

Everything can be classified and discussed. First of all, it is divided into two categories: the object me has the name attribute and there is no name attribute.

Of course, we all know that there is a prototype chain mechanism in JavaScript, so when the name attribute does not exist in the me , it can also be divided into two situations: me name attribute and the 061b9c3c070357 attribute does not exist on the prototype chain of name . It can be divided into three categories to discuss.

  1. name attribute already exists in me

    const me = {
        name: 'me',
    };
    me.name = 'forceddd';
    
    console.log(me.name); //forceddd

In this case, it is obvious that our purpose is to reset name attribute, and the result seems to be obvious. me.name been modified to 'forceddd' .

But don’t forget that the attributes of an object also have its own attributes, including whether it is read-only ( writable ), (usually an object used to describe the various characteristics of an object’s attributes is called The attribute descriptor or attribute description object 161b9c3c07059b), so when the writable ) is false , what will the result be like? Not only that, the name attribute defines the getter or setter , which becomes a access descriptor , what will the result be like?

  • name property writable is false time

    const me = {
        name: 'me',
    };
    Object.defineProperty(me, 'name', {
        writable: false,
        value: me.name,
    });
    
    me.name = 'forceddd';
    // name的属性值仍是 'me'
    console.log(me.name); //me
 因为 `name` 属性是只读的的,所以赋值操作 `me.name = 'forceddd'` 失败了,这恒河狸。但是要注意的是,这种操作失败是静默的,如果我们在操作之后没有校验 `name` 值的话,是很难发现的。使用严格模式可以把这种静默失败变成显式的**TypeError**。

 ```js
 'use strict';//使用严格模式
 const me = {
     name: 'me',
 };
 
 Object.defineProperty(me, 'name', {
     writable: false,
     value: me.name,
 });
 me.name = 'forceddd';
 //TypeError: Cannot assign to read only property 'name' of object '#<Object>'
 ```
  • When the name attribute is an access descriptor ( getter or setter defined)

    const me = {
        _name: 'me',
        get name() {
            return this._name;
        },
        set name(v) {
            console.log('调用setter,设置name');
            this._name = v;
        },
    };
    
    me.name = 'forceddd';//调用setter,设置name
    
    console.log(me.name);//forceddd
 此时,`name` 属性存在 `setter` 函数,在进行赋值操作时,就会调用 `setter` 函数。一般来说,我们在定义访问描述符时,`getter ` 和 `setter `都是**成对出现**的,但是只定义其中一个也是没有任何问题的,只是这样的话可能会出现一些我们不期望的情况,比如,当 `name` 只定义了一个 `getter` 函数时,不存在 `setter` 函数,赋值操作便没有意义了。

 **非严格模式**下,因为没有 `setter` 函数,所以赋值静默失败了。

 ```js
 const me = {
     _name: 'me',
     get name() {
         return this._name;
     },
 };
 
 me.name = 'forceddd';
 
 console.log(me.name); //me
 ```

 **严格模式**下,对没有 `setter` 函数的访问描述符进行赋值操作,会出现一个**TypeError**,也是非常河狸的。

 ```js
 'use strict';
 const me = {
     _name: 'me',
     get name() {
         return this._name;
     },
 };
 
 me.name = 'forceddd';
 //TypeError: Cannot set property name of #<Object> which has only a getter
 ```

To sum up, when the me attribute exists in name , there are three situations when performing the assignment operation:

  1. When the attribute is a Access Descriptor , if present setter , is called setter ; if there is no setter , in the non-strict mode silent failures, in strict mode generates a TypeError .
  2. writable of the attribute descriptor (061b9c3c0708e4) is false , it will fail silently in non-strict mode, and a TypeError
  3. When the attribute does not belong to the above two cases, the value is set to the value of the attribute, and the assignment is successful.
  1. not exist on 161b9c3c0709c1 me and its prototype chain 161b9c3c0709e0

    At this time, a new attribute name will be created on the object me with a value of 'forceddd' . This is also a way we often use.

    const human = {};
    const me = {};
    Object.setPrototypeOf(me, human);
    me.name = 'forceddd';
    
    console.log({ me, human }); //{ me: { name: 'forceddd' }, human: {} }

The attribute we created in this way can of course be modified by assignment, which means that writable in the attribute descriptor of this attribute at this time is true . In fact, at this time attributes configurability , enumerable of also as true , and by which defineProperty adding attribute default values are different, defineProperty method default attribute descriptor added in writable , configurable , enumerable default values of false are all 061b9c3c070acd. This is also a point worth noting.

console.log(Object.getOwnPropertyDescriptor(me, 'name'));
// {
//   value: 'forceddd',
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Add attributes through the defineProperty

Object.defineProperty(me, 'prop', { value: 'forceddd' });
console.log(Object.getOwnPropertyDescriptor(me, 'prop'));
// {
//   value: 'forceddd',
//   writable: false,
//   enumerable: false,
//   configurable: false
// }
  1. name attribute does not exist in

    At this point, me prototype object human presence on name property, very often, we use me.name = 'forceddd' assignment statement like, just to modify the object me , but did not want to involve other objects. But in JS, when accessing object properties, if the property does not exist on the object, it will go to the prototype chain to find it, so the prototype chain of the object is difficult to bypass.

    const human = {};
    const me = {};
    Object.setPrototypeOf(me, human);

As mentioned earlier, there are also three cases for the name attribute on the human

  • name attribute descriptor attribute writable is false

    //设置human对象的name属性
    Object.defineProperty(human, 'name', {
        writable: false,
        value: 'human',
    });
    me.name = 'forceddd';
    console.log(me);//{}
 此时,当我们进行了赋值操作后,检查 `me` 对象,会发现它并没有 `name` 属性。WTF?很难理解是吧,因为 `human` 中的 `name` 属性是只读的,所以将 `human` 对象为原型的对象就不能通过 `=` 赋值操作新增 `name` 属性了。

 其实这是为了**模仿类的继承行为**,如果父类中的 `name` 属性是只读的,那么继承它的子类中的 `name` 属性自然应该也是只读的。又因为在JS中时通过原型链来模拟的类的继承行为,所以就导致了这一看起来很奇怪的现象。

 同样地,如果是在**严格模式**下,不会是静默失败了,而是会产生一个**TypeError**。
  • name attribute is a access descriptor

    Object.defineProperty(human, 'name', {
        set() {
            console.log('调用了human的setter');
        }
    });
    me.name = 'forceddd';//'调用了human的setter'
    console.log(me);//{}
 此时,同样不会在 `me` 对象上创建 `name` 属性,而是会调用 `human` 上 `name` 属性的 `setter` 函数。类似地,当只存在`getter`,**不存在** `setter` 时,**严格模式**下会产生**TypeError**,**非严格模式**下会静默失败。
  • name attribute is not in the previous two cases

    This situation is the simplest and most in line with our expectations. A name me

To summarize: When the me attribute does not exist in name name attribute exists on the prototype chain, there are three situations:

  1. If the property is in a prototype chain Access Descriptor and there setter , this will be called setter ; if there is only getter , the non-strict mode silently fail with strict mode TypeError .
  2. If the attribute in the prototype chain is a writable being false , it will fail silently in non-strict mode, and TypeError will appear in strict mode.
  3. If the attribute is not the first two cases on the prototype chain, but a writable as true , the attribute will be created on the me object.

If you must, must be modified or set me of name property, you can use defineProperty ways to circumvent = these restrictions assignment.

Object.defineProperty(human, 'name', {
    set() {
        console.log('调用了human的setter');
        // me.name = 'forceddd';
    },
});
Object.defineProperty(me, 'name', {
    value: 'forceddd',
    enumerable: true,
    writable: true,
    configurable: true,
});
console.log(me); //{ name: 'forceddd' }

The description on the text is always not so intuitive, and finally summarized into a picture, which is easy to understand.


forceddd
271 声望912 粉丝

一名前端爱好者。