手写 new 操作符

221 阅读2分钟

在 ES5 的时候,我们经常使用 new 操作符来调用函数从而创建一个对象,这时候,函数被称为对象的构造函数,一般函数名的首字母需要大写,那么 new 到底做了什么神奇的事情,能够让函数在不返回任何值的情况下, 返回一个对象,并且该对象还是构造函数内部的 this 呢?不要走开,本文将把这些谜底一一揭晓!

new 的4件事

new 操作符在构造函数内部主要做了4件事:

  1. 首先会创建一个空对象 — newObject
  2. 将对象的 __proto__ 属性指向构造函数的 prototype 属性
  3. 将构造函数的 this 指向新对象
  4. 判断构造函数的返回值类型,如果返回的是非原始值,那么就作为 new 操作符的最终结果,否则就是 newObject 作为 new 操作符的最终结果。

实现原理

了解清楚 new 做了哪些事情之后,我们就来马上实现一下 new 的原理吧!

function myNew() {
  const constructorFn = Array.prototype.shift.call(arguments);
  // 判断参数是否是一个函数
  if (typeof constructorFn !== "function") {
    console.error("type error");
    return;
  }
  // 新建一个空对象,对象的__proto__指向构造函数的prototype (第1步和第2步)
  const newObject = Object.create(constructorFn.prototype);
  
  // 将 this 指向新建对象,并执行函数 (第3步)
  const result = constructorFn.apply(newObject, arguments);
 
  // 如果构造函数返回的对象,则返回该值。(第4步)
  const typeResult = typeof result
  if (typeResult !== 'null' &&  (typeResult === "object" || typeResult === "function")) {
      return result;
  }
  // 否则返回新对象 (第4步)
  return newObject;
}

// 使用方法
function User(name, age) {
    this.name = name;
    this.age = age;
}
User.prototype.introduce = function () {
    return `My name is ${this.name}, ${this.age} years old.`;
};
const user1 = myNew(User, 'jack', 18);
console.log(user1); // {name: 'jack', age: 18}
console.log(user1.introduce()) // My name is jack, 18 years old.

实际上,new 的4件事对应了3行代码,就是上面源代码标注的第1步,第2步,第3步和第4步,所以总的来说,我们实现 new 的原理只需要3行代码!