JavaScript属性描述对象

/**
 * JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为;
 * 这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。
 */

var p =  {
    value: 123,     // 该属性的属性值,默认为undefined
    writable: false,    // 布尔值,表示属性值(value)是否可改变(即是否可写),默认为true。
    enumerable: true,   // 布尔值,表示该属性是否可遍历,默认为true。如果设为false,会使得某些操作(比如for...in循环、Object.keys())跳过该属性。
    configurable: false,    // 布尔值,表示属性的可配置性,默认为true。如果设为false,将阻止某些操作改写属性描述对象,比如无法删除该属性,也不得改变各种元属性(value属性除外)
    get: undefined,     // 一个函数,表示该属性的取值函数(getter),默认为undefined。
    set: undefined      // 一个函数,表示该属性的存值函数(setter),默认为undefined。
}

// Object.getOwnPropertyDescriptor():获取属性描述对象。只能用于对象自身的属性,不能用于继承的属性
Object.getOwnPropertyDescriptor(obj, 'p')
// {
//     "value": "a",
//     "writable": true,
//     "enumerable": true,
//     "configurable": true
// }

// Object.getOwnPropertyNames():返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历。
Object.getOwnPropertyNames(p)
// [
//     "value",
//     "writable",
//     "enumerable",
//     "configurable",
//     "get",
//     "set"
// ]

// Object.defineProperty():允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象
Object.defineProperty(object, propertyName, attributesObject)
// object:属性所在的对象
// propertyName:字符串,表示属性名
// attributesObject:属性描述对象
var obj = Object.defineProperty({}, 'p', {
  value: 123,
  writable: false,
  enumerable: true,
  configurable: false
});
obj.p // 123
obj.p = 246;
obj.p // 123

// 一次性定义或修改多个属性,使用 Object.defineProperties()
var obj = Object.defineProperties({}, {
  p1: { value: 123, enumerable: true },
  p2: { value: 'abc', enumerable: true },
  p3: { get: function () { return this.p1 + this.p2 },  //p3属性定义了取值函数get,即每次读取该属性,都会调用这个取值函数
    enumerable:true,
    configurable:true
  }
});
obj.p1 // 123
obj.p2 // "abc"
obj.p3 // "123abc"

// Object.prototype.propertyIsEnumerable()返回一个布尔值,用来判断对象自身某个属性是否可遍历。
var obj = {};
obj.p = 123;
obj.propertyIsEnumerable('p') // true
obj.propertyIsEnumerable('toString') // false  obj.toString是继承的属性

// 存取器的常用写法
// 写法二
var obj = {
  get p() {
    return 'getter';
  },
  set p(value) {
    console.log('setter: ' + value);
  }
};