每天搞透一道JS手写题💪「Day5创建新对象的那些事(new、Object.create、Object.assign)」

346 阅读3分钟
方法描述用法
new创建一个新的对象实例。let obj = new Object();
Object.create()创建一个新对象,使用现有的对象作为新创建对象的原型。let obj = Object.create(proto, propertiesObject);
Object.assign()复制一个或多个源对象的所有可枚举自身属性到目标对象。let obj = Object.assign(target, source1, source2);

new

我们平时是怎么使用 new 关键字的呢?

  • 首先,new 需要跟一个构造函数来创建它的实例,如果 new 后面跟的不是构造函数会抛出类型错误:xxx is not a constructor
  • 其次,这个构造函数可以传参;

对于 new 关键字进行的操作,new 运算符 文档中有很详细的描述。

new 关键字会进行如下的操作:

  1. 创建一个空的简单 JavaScript 对象(即  {} );
  2. 为步骤 1 新创建的对象添加属性  __proto__ ,将该属性链接至构造函数的原型对象;
  3. 将步骤 1 新创建的对象作为 this 的上下文;
  4. 如果该函数没有返回对象,则返回 this
function myNew(constructor, ...args) {
    // 判断参数是否是一个函数
    if (typeof constructor !== "function") {
        throw new TypeError(`${constructor} is not a constructor`)
    }
    // 创建一个新对象
    const obj = {};
    // 将新对象的原型设置为构造函数的prototype
    obj.__proto__ = constructor.prototype;
    // 调用构造函数,并将this绑定到新创建的对象上
    const result = constructor.apply(obj, args);
    // 检查构造函数的返回值
    if (result !== null && (typeof result === 'object' || typeof result === 'function')) {
        return result;
    }
    return obj;
}

Object.create

Object.create()  静态方法以一个现有对象作为原型,创建一个新对象。它接受两个参数:

  • proto 新创建对象的原型对象。
  • propertiesObject 可选,如果该参数被指定且不为 undefined,则该传入对象可枚举的自有属性将为新创建的对象添加具有对应属性名称的属性描述符。这些属性对应于 Object.defineProperties() 的第二个参数。

如果 proto 既不是 null,也不是 Object,则抛出类型错误。如果 proto 是 null,会被视为这个新对象没有原型。

Object.myCreate = function(proto, propertiesObject) {
  // 检查传入的原型对象是否为一个对象
  if (typeof proto !== 'object' && typeof proto !== 'function') {
      throw new TypeError('Object prototype may only be an Object or null: ' + proto);
  }
  if ( defineProperties === null ) {
    throw new TypeError('Cannot convert undefined or null to object')
  }
  // 创建一个新对象
  const obj = {};
  // 将新对象的原型设置为传入的原型对象
  obj.__proto__ = proto;
  // 如果有传入属性描述对象,则为新对象定义这些属性
  if (propertiesObject !== undefined) {
      Object.defineProperties(obj, propertiesObject);
  }
  // 返回新创建的对象
  return obj;
}

Tip: 使用 Object.create(null) 可以得到一个绝对干净的空对象,不会自原型链上继承属性。

Object.assign

Object.assign()  静态方法将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。如果目标对象与源对象具有相同的键(属性名),则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的同名属性。

/**
 * 自定义的Object.assign方法
 * @param {Object} target - 目标对象
 * @param {...Object} sources - 源对象
 * @returns {Object} 返回目标对象
 */
Object.myAssign = function(target, ...sources) {
    // 检查target是否为null或者一个对象
    if (target === null || target === undefined) {
        throw new TypeError('Cannot convert undefined or null to object');
    }

    // 将target转换为对象
    let to = Object(target);

    // 遍历源对象
    for (let source of sources) {
        // 将源对象转换为对象
        let from = Object(source);

        // 获取源对象的所有自身可枚举属性键
        let keys = Object.keys(from);

        // 遍历属性键
        for (let key of keys) {
            // 使用源对象的属性值覆盖目标对象的属性值
            to[key] = from[key];
        }
    }

    // 返回目标对象
    return to;
}