对象

属性的可枚举与遍历

可枚举

  • 对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。
  • Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }
  • 描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性。
  • 目前,有四个操作会忽略enumerable为false的属性。

    • for...in循环:只遍历对象自身的和继承的可枚举的属性。
    • Object.keys():返回对象自身的所有可枚举的属性的键名。
    • JSON.stringify():只串行化对象自身的可枚举的属性。
    • Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性(包括 Symbol)。

其中,只有for...in会返回继承的属性,其他三个方法都会忽略继承的属性,只处理对象自身的属性

遍历

  • ES6 一共有 5 种方法可以遍历对象的属性。

    • (1) for...in

      • for...in 循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
    • (2) Object.keys(obj)

      • Object.keys 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
    • (3) Object.getOwnPropertyNames(obj)

      • Object.getOwnPropertyNames 返回一个数组,包含对象自身的所有属性(不含 Symbol 属性)的键名。
    • (4) Object.getOwnPropertySymbols(obj)

      • Object.getOwnPropertySymbols 返回一个数组,包含对象自身的所有 Symbol 属性的键名。
    • (5) Reflect.ownKeys(obj)

      • Reflect.ownKeys 返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

Object.is()

  • ES5 之前只有两种运算符 == 和 ===.
    • == 会自动转换类型
    • === 后者 NaN !== NaN, 同时 +0 === -0
  • ES6 提出同值相等, Object.is 就是该算法的实现, 解决了 === 的问题 Object.is(NaN, NaN) // true, Object.is(-0, +0) // false
Object.defineProperty(Object, 'is', {
  value: function(x, y) {
    if (x === y) {
      // 针对+0 不等于 -0的情况
      return x !== 0 || 1 / x === 1 / y;
    }
    // 针对NaN的情况
    return x !== x && y !== y;
  },
  configurable: true,
  enumerable: false,
  writable: true
});

Object.assign

  • Object.assign() 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
  • 浅拷贝
  • 数组对象也可以 Object.assign([1,2,3], [4,5]) // [4,5,3]

Object.getPrototypeOf

Object.create()

  • Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的proto
  • 使用Object.create()来实现类式继承
// Shape - 父类(superclass)
function Shape() {
  this.x = 0;
  this.y = 0;
}

// 父类的方法
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

// Rectangle - 子类(subclass)
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// 子类续承父类
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

console.log('Is rect an instance of Rectangle?',
  rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
  rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'

Object.getOwnPropertyDescriptors()

  • 返回某个对象属性的描述对象(descriptor)
const obj = {
  foo: 123,
  get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

Object.defineProperties()

  • 直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
  • Object.defineProperties(obj, props)

proto 属性

  • 指向对象原型的 prototype, 属于浏览器实现
  • 如果一个对象本身部署了proto属性,该属性的值就是对象的原型。
Object.defineProperty(Object.prototype, '__proto__', {
  get() {
    let _thisObj = Object(this);
    return Object.getPrototypeOf(_thisObj);
  },
  set(proto) {
    if (this === undefined || this === null) {
      throw new TypeError();
    }
    if (!isObject(this)) {
      return undefined;
    }
    if (!isObject(proto)) {
      return undefined;
    }
    let status = Reflect.setPrototypeOf(this, proto);
    if (!status) {
      throw new TypeError();
    }
  },
});

function isObject(value) {
  return Object(value) === value;
}

Object.setPrototypeOf()

  • 作用与 proto 类似, 用来设定对象的原型对象(prototype)
  • 如果参数不是对象,会被自动转为对象。 但是由于回传结果是第一个参数本身, 所以还是等于自身
// 格式
Object.setPrototypeOf(object, prototype)

// 用法
const o = Object.setPrototypeOf({}, null);

// 等价于
function setPrototypeOf(obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

Object.getPrototypeOf()

  • 用于读取对象的原型对象
  • 如果参数不是对象,会被自动转为对象。

不管是 Object.getPrototypeOf() 还是 Objcet.setPrototypeOf() 如果第一个参数是 null / undefined 都会报错

Q&A

const obj = Object.create({}, {p:
  {
    value: 42,
    enumerable: true
  }
});
Object.values(obj) // [42]

// {p: {value: 42}} 为什么不会当成一个普通对象
// Object.create 作用不是绑定原型对象, 那不就是继承属性, Object.values 为什么可以访问的到





以往
JSRUN前端笔记, 是针对前端工程师开放的一个笔记分享平台,是前端工程师记录重点、分享经验的一个笔记本。JSRUN前端采用的 MarkDown 语法 (极客专用语法), 这里属于IT工程师。