重新认识JavaScript赋值表达式

375 阅读5分钟

当我使用Chrome打开了一个网页,然后打开了开发者工具,然后在Console面板输入obj.name = 1;,按下回车后,执行这样一段赋值表达式。

  • 在控制台会得到什么样的执行结果?
  • 会带来什么副作用?

1. obj是null或undefined

var obj = null;
// var obj = undefined;

obj.name = 1;

表达式执行的结果值

  • 抛出异常

副作用

  • 抛出异常

2. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? false >>> 对象isExtensible ? false

'use strict';
var obj = {};
Object.preventExtensions(obj);

obj.name = 1;

表达式执行的结果值

  • 严格模式下抛出异常
  • 非严格模式下为1

副作用

  • obj不会增加属性

3. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? false >>> 对象isExtensible ? true

var obj = {};

obj.name = 1;

表达式执行的结果值

  • 1

副作用

  • obj被添加了一个key值为name的值属性,属性描述为 {"value":1,"writable":true,"enumerable":true,"configurable":true}

4. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? true >>> 属性是存储器属性或值属性 ? 存储器属性 >>> 存储器属性设置了setter函数 ? false

var obj = Object.defineProperty({}, 'name', {
  get(val) {
    console.log('get value: ', val)
  },
  set: undefined,
})

obj.name = 1;

表达式执行的结果值

  • 1

副作用

  • 无副作用

5. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? true > 属性是存储器属性或值属性 ? 存储器属性 > 存储器属性设置了setter函数 ? true

var obj = {
  set name(val) {
    console.log('set value: ', val);
    // throw new Error('error')
  }
};

obj.name = 1;

表达式执行的结果值

  • 1抛出异常

副作用

  • 取决于setter函数带来的副作用

6. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? true >>> 属性是存储器属性或值属性 ? 值属性 >>> 对象自身或原型链对象中的该属性是可写的 ? false >>> 原有属性是自身属性或是原型链中原型对象的 ?自身属性

// 'use strict';
var obj = Object.defineProperty({}, 'name', {
  writable: false,
})

obj.name = 1;

表达式执行的结果值

  • 严格模式下抛出异常
  • 非严格模式下为1

副作用

  • obj不会被添加属性

7. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? true >>> 属性是存储器属性或值属性 ? 值属性 >>> 对象自身或原型链对象中的该属性是可写的 ? false >>> 原有属性是自身属性或是原型链中原型对象的 ? 原型链对象对象中的属性

// 'use strict';
var objPrototype = Object.defineProperty({}, 'name', {
  writable: false,
})
var obj = Object.create(objPrototype);

obj.name = 1;

表达式执行的结果值

  • 严格模式下抛出异常
  • 非严格模式下为1

副作用

  • obj不会被添加属性

8. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? true >>> 属性是存储器属性或值属性 ? 值属性 >>> 对象自身或原型链对象中的该属性是可写的 ? true >>> 原有属性是自身属性或是原型链中原型对象的 ?自身属性

var obj = Object.defineProperty({}, 'name', {
  writable: true,
  value:10,
})

obj.name = 1;

表达式执行的结果值

  • 1

副作用

  • 修改obj对象key值为name的属性的属性描述的value值为1

9. obj是原始值或复合值 ? 复合值 >>> 对象自身或者原型链中的对象hasOwnProperty('name') ? true >>> 属性是存储器属性或值属性 ? 值属性 >>> 对象自身或原型链对象中的该属性是可写的 ? true >>> 原有属性是自身的或来自原型链中的原型对象 ?原型链中的原型对象

var objPrototype = Object.defineProperty({}, 'name', {
  writable: true,
})
var obj = Object.create(objPrototype);

obj.name = 1;

表达式执行的结果值

  • 1

副作用

  • obj被添加了一个key值为name的值属性,属性描述为 {"value":1,"writable":true,"enumerable":true,"configurable":true}

对原始值的操作

  • JavaScript中的原始值的类型有

string,number,boolean,bigint,symbol

假设obj是一个原始值,那么obj.name=1表达式执行的结果值的可能性。

返回1

var obj = 100;
obj.name = 1;

抛出异常

'use strict';
var obj = 100;
Object.defineProperty(Number.prototype, 'name', {
  writable: false,
});
obj.name = 1;

表达式的副作用的可能性

无副作用

var obj = 100;
obj.name = 1;

来自setter函数的副作用

var obj = 1;
var nameCounter = 0;
Object.defineProperty(Number.prototype,'name',{
  set(val){
    console.log('set val: ',val);
    nameCounter += 1;
  }
});
obj.name = 1;

其他可能性参照上述obj是复合值的情况

其他情况

上述所表述的场景基本能涵盖大多数的应用场景,还有一种特殊情况是顶层对象拥有一个key值obj的存储器属性

var rawObj = { _name:'rawObj' };
Object.defineProperty(window, 'obj', {
  get() {
    console.log('get rawObj');
    return rawObj;
  },
  set(val) {
    console.log('set value: ', val)
  },
  enumerable: true,
  configurable: true,
});
obj.name = 1;
  • obj.name = 1的副作用是会令rawObj添加一个key值为name的值属性,属性描述为 {"value":1,"writable":true,"enumerable":true,"configurable":true}

obj是proxy的情况

var obj = new Proxy({}, {
  get: function (target, propKey, receiver) {
    console.log(`getting ${propKey}!`);
    return Reflect.get(target, propKey, receiver);
  },
  set: function (target, propKey, value, receiver) {
    console.log(`setting ${propKey}!`);
    return Reflect.set(target, propKey, value, receiver);
  }
});
obj.name = 1;