new 操作符的实现原理

理解 new 操作符的执行过程并手写实现

问题

JavaScript 中 new 操作符具体做了什么?如何手写实现一个 new 操作符?

解答

new 操作符的作用

new 操作符用于创建一个给定构造函数的实例对象。

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.sayName = function() {
    console.log(this.name);
}

const person1 = new Person('Tom', 20);
console.log(person1); // Person {name: "Tom", age: 20}
person1.sayName(); // 'Tom'

通过 new 创建的实例可以:

  • 访问构造函数中的属性
  • 访问构造函数原型链中的属性

返回值的处理

构造函数返回原始类型时,返回值会被忽略:

function Test(name) {
  this.name = name;
  return 1;
}
const t = new Test('xxx');
console.log(t.name); // 'xxx'

构造函数返回对象时,返回值会被正常使用:

function Test(name) {
  this.name = name;
  return { age: 26 };
}
const t = new Test('xxx');
console.log(t); // { age: 26 }
console.log(t.name); // undefined

执行流程

new 操作符的执行步骤:

  1. 创建一个新的空对象
  2. 将新对象的原型指向构造函数的 prototype
  3. 执行构造函数,将 this 绑定到新对象上
  4. 根据构造函数返回值判断:如果是对象则返回该对象,否则返回新创建的对象

手写实现

function mynew(Func, ...args) {
    // 1.创建一个新对象
    const obj = {};
    // 2.新对象原型指向构造函数原型对象
    obj.__proto__ = Func.prototype;
    // 3.将构造函数的 this 指向新对象
    let result = Func.apply(obj, args);
    // 4.根据返回值判断
    return result instanceof Object ? result : obj;
}

测试代码:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.sayName = function() {
    console.log(this.name);
}

const person1 = mynew(Person, 'Tom', 20);
console.log(person1); // Person {name: "Tom", age: 20}
person1.sayName(); // 'Tom'

关键点

  • new 操作符会创建一个新对象,并将其原型指向构造函数的 prototype
  • 构造函数内的 this 会被绑定到新创建的对象上
  • 如果构造函数返回对象,则使用该返回值;返回原始类型则忽略
  • 实现 new 的核心是原型链连接和 this 绑定