「Object.create」详解 之 源码彻底吃透

532 阅读2分钟

Object.create是什么?

MDN的定义:Object.create()方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。

语法

Object.create(proto, propertiesObject)

proto

  • 新创建对象的原型对象。

propertiesObject (可选)

  • 如果该参数被指定且不为 undefined,则该传入对象的自有可枚举属性将为新创建的对象添加指定的属性值和对应的属性描述符。这些属性对应于 Object.defineProperties() 的第二个参数,也就是enumerable。

使用 null 原型的对象

1.以 null 为原型的对象存在不可预期的行为,因为它未从 Object.prototype 继承任何对象方法,即创建对象的原型为空。

const normalObj = {};   
const nullProtoObj = Object.create(null); 

图片.png

2.使用null创建原型对象的时候,再使用Object的原型对象的方法就会报错,因为原型上不存在该方法了;

const normalObj = {};   
const nullProtoObj = Object.create(null); 

normalObj.valueOf(); // {}
nullProtoObj.valueOf(); //error:nullProtoObj.valueOf is not a function

图片.png

3.我们可以为以 null 为原型的对象添加 toString 方法,类似于这样:

nullProtoObj.toString = Object.prototype.toString;
console.log(nullProtoObj); // toString: ƒ toString()

与常规的对象不同,nullProtoObjtoString 方法是这个对象自身的属性,而非继承自对象的原型。这是因为 nullProtoObj “没有”原型(null)。

4.在实践中,Object.prototype原型的自有属性可能会导致我们判断错误

const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return name in ages;
}

function getAge(name) {
  return ages[name];
}

hasPerson("hasOwnProperty") // true
getAge("toString") // [Function: toString]

使用null创建对象则消除了这种潜在的问题

const ages = Object.create(null, {
  alice: { value: 18, enumerable: true },
  bob: { value: 27, enumerable: true },
});

hasPerson("hasOwnProperty") // false
getAge("toString") // undefined

Object.create的使用示例

let o = Object.create(
  {},
  {
    p: {
      value: 42,
      writable: false,//可写的 -- 为true时o对象的属性值可改变
      enumerable: false,//可枚举的 -- 为true时o对象的属性可被遍历
      configurable: false,//可配置的 -- 为true时o对象的属性可被删除
    },
  }
);

o.p = 24; //{p: 42}
delete o.p; //{p: 42}
for (let i in o) {
  console.log(i);// i 没有输出
}

源码-示例

/* 源码
 ------------------------------*/
Object.myCreate = function (proto, propertiesObject = undefined) {
  if (propertiesObject === null) {
    // 这里没有判断propertiesObject 是否是原始包装对象
    throw "Cannot convert undefined or null to object at Function.create (<anonymous>)";
  } else {
    function Fn() {}
    Fn.prototype = proto;
    let obj = new Fn();
    if (proto === null) {
      obj.__proto__ = null; 
    }
    if (propertiesObject !== undefined) {
      Object.defineProperties(obj, propertiesObject);
    }
    return obj;
  }
};

/* 示例
 ------------------------------*/
const throwError = Object.myCreate({}, null); //throwError

const obj1 = Object.myCreate(null);
console.log(obj1);

const obj2 = Object.myCreate({ age: 18 });
console.log(obj2); //  obj2的构造函数的原型对象是age: 18

const obj3 = Object.myCreate(
  { age: 18 },
  {
    isTrue: {
      value: true,
      enumerable: true,
    },
  }
);
console.log(obj3); // isTrue: true, obj3的构造函数的原型对象是age: 18

Object.create的兼容性

图片.png

如有小伙伴发现错误之处欢迎指正🤚