详解Object.create

1,521 阅读2分钟

Object.create 的作用?

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

Object.create(proto, [propertiesObject])

  • proto: 新创建对象的原型对象
  • propertiesObject: 可选。要添加到新对象的可枚举(新添加的属性是自身的属性,而不是其原型链上的属性)的属性
const obj = Object.create(Object.prototype, {
  // foo会成为新创建对象的数据属性
  foo: {
    writable: true,
    configurable: true,
    value: "this is foo",
  },
  // bar会成为所创建对象的访问器属性
  bar: {
    configurable: false,
    get: function () {
      return 10;
    },
    set: function (value) {
      console.log("Setting to ", value);
    },
  },
});

Object.create 和 {...}的区别?

const o = {};

先看下常用的{}对象

自身是没有属性的,但是它的原型对象__proto__继承了Object的各种方法,

来看看Object.create()创建的对象

const o = Object.create(null);

通过Object.create(null)创建的对象,对象没有任何的属性

再来看看Object.create({})创建的对象

const o = Object.create({});

这里可以看出创建的对象已经跟{}很像了,只不过是多了层__proto__,这是因为{}实例也是通过__proto__继承Object.prototype的方法,所以这里o.__proto__.__proto__ === Object.prototype 是正确的。

使用场景?

  • 创建一个纯净的对象Object.create(null),自己可以定义想要的方法,如toString
  • 复制一份目标对象的方法,而后在修改自身对象时避免污染源对象Object.create(Array.prototype)
例如Vue源码在处理数组响应式的时候, 针对数组方法做了修改, 就是用到了Object.create;

const arrayProto = Array.prototype;
创建一个新的数组对象, 修改该对象的数组的七个方法, 防止污染原生数组方法;
const arrayMethods = Object.create(arrayProto)
[("push", "pop", "shift", "unshift", "splice", "sort", "reverse")].forEach(
  (method) => {
    /*将数组的原生方法缓存起来,后面要调用*/
    const original = arrayProto[method];
    def(arrayMethods, method, function mutator() {
      // avoid leaking arguments:
      // http://jsperf.com/closure-with-arguments
      let i = arguments.length;
      const args = new Array(i);
      while (i--) {
        args[i] = arguments[i];
      }
      /*调用原生的数组方法*/
      const result = original.apply(this, args);

      /*数组新插入的元素需要重新进行observe才能响应式*/
      const ob = this.__ob__;
      let inserted;
      switch (method) {
        case "push":
          inserted = args;
          break;
        case "unshift":
          inserted = args;
          break;
        case "splice":
          inserted = args.slice(2);
          break;
      }
      if (inserted) ob.observeArray(inserted);

      // notify change
      /*dep通知所有注册的观察者进行响应式处理*/
      ob.dep.notify();
      return result;
    });
  }
);

如何实现一个 Object.create

Object.create 是ES6的方法, ES5及以下是无法使用的, 那么就需要实现一个polyfill吧.


Object.create = (proto, propertiesObject) => {
  // 属性为null,抛出异常
  if (propertiesObject === null) throw 'TypeError'
  function F () {}
  // 指定原型对象
  F.prototype = proto
  const o = new F()
  // 这里是第二个选项, 添加属性
  if (typeof propertiesObject !== 'undefined') {
    Object.defineProperties(o, propertiesObject)
  }
  // 如果proto 为null,需要去除__proto__,实现纯净的map
  if (proto === null) o.__proto__ = null
  // 返回新的对象
  return o
}