属性描述符

属性描述符

在ES5开始,全部属性都具有了属性描述符。

咱们能够经过Object.getOwnPropertyDescriptor来观察到属性的描述符,它是长这样子的。app

Object.getOwnPropertyDescriptor({a: 1}, 'a');

// {
//   value: 1,
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

writable决定该属性是否只读。
enumerable决定该属性是否可枚举。
configurable决定该属性是否可从新设置描述符。this

属性描述符的设置

当属性的configurable为true时,咱们能够经过Object.defineProperty来修改属性描述符。code

'use strict';

var foo = {a: 1};

for(var i in foo) {console.log(i)};
// 'a'

Object.defineProperty(foo, 'a', {
  writable: false,
  enumerable: false,
  configurable: false
});

foo.a = 2;// Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'

for(var i in foo) {console.log(i)};
// 'nothing happend'

Object.defineProperty(foo, 'a', { // Uncaught TypeError: Cannot redefine property: a
  configurable: true
});

访问描述符(getter和setter)

getter和setter有两种方式定义对象

字面量定义ip

var foo = {
  get a(){
    return this._a;
  },
  set a(value){
    this._a = value;
  }
};

使用Object.defineProperty定义原型链

var foo = {a: 1};

Object.defineProperty(
  foo,
  'a',
  {
    get(){
      return this._a;
    },
    set(value){
      this._a = value;
    }
  }
);

这个时候再看看属性a的描述符。get

Object.getOwnPropertyDescriptor(foo, 'a')

// {
//   get: ƒ a(),
//   set: ƒ a(value),
//   enumerable: true,
//   configurable: true
// }

当出现gettersetter时,valuewritable就会失效。
此时,属性a的描述符被称为访问描述符
访问描述符属性描述符互斥,若是此时再从新设置value或者writable描述符,settergetter也会被丢弃。原型

屏蔽属性

当你给一个对象赋值一个新的属性 foo时,若是该对象的原型链上已存在属性 foo,而且 foo被标记为只读( writable: false)时,严格模式下会抛出异常,非严格模式下,这条赋值语句会被忽略。这种属性称为 屏蔽属性

举个例子it

'use strict';

var anotherObject = {};

Object.defineProperty(anotherObject, 'foo', {// 将anotherObject的foo属性设为只读
  value: 1,
  writable: false
});

var myObject = Object.create(anotherObject);// 将myObject的原型设置为anotherObject

console.log(myObject);// {}
console.log(myObject.foo);// 1

myObject.foo = 2;// Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'

还有一种状况console

当你给一个对象赋值一个新的属性 foo时,若是该对象的原型链上已存在属性 foo,而且 foo被设置了 setter时,将会调用这个 setter,而且该赋值语句将会被忽略,此时也会发生属性屏蔽。
var anotherObject = {};

Object.defineProperty(anotherObject, 'foo', {// 给anotherObject的foo属性设置setter
  set(value) {
    console.log(value);
    this._foo = value;
  },
  get() {
    return this._foo;
  }
});

var myObject = Object.create(anotherObject);// 将myObject的原型设置为anotherObject

myObject.foo = 2;// 此时会触发anotherObject.foo的setter,控制台输出2

console.log(Object.hasOwnProperty(myObject, 'foo'));// false

解决方案

使用Object.defineProperty来添加属性。