手写New操作符

79 阅读2分钟

new的用法

首先我们用new关键字来创建构造函数/类的实例,比如:

// 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
const p = new Person('张三', 20);
console.log(p); // Person { name: '张三', age: 20 }
console.log(p instanceof Person); // true(之前学的instanceof,刚好呼应!)

// ES6类(本质也是构造函数)
class Student {
  constructor(grade) {
    this.grade = grade;
  }
}
const s = new Student(3);
console.log(s.grade); // 3

这就是new关键字的用法。

手写new

核以 const p = new Person("张三", 20) 为例,对应 4 步:

  1. 创建空对象:const obj = {}
  2. 绑定原型:obj.__proto__ = Person.prototype(或 Object.create(Person.prototype));
  3. 绑定 this 执行:Person.apply(obj, ["张三", 20]) → 此时 obj 变成 { name: "张三", age: 20 }
  4. 判断返回值:Person 没有显式返回值(默认返回 undefined,值类型),所以最终返回 obj → 就是 p

我们就可以照着写一个自己的 new起名叫 objectFactory (工厂函数),用法和 new 一样: objectFactory(构造函数, 参数1, 参数2)

function objectFactory() {
  // 1. 从 arguments 中取出构造函数(第一个参数)
  const constructor = Array.prototype.shift.call(arguments);
  // 解释:Array.prototype.shift 会删除并返回第一个元素,这里把 arguments 转成类数组再调用 shift
  // 比如 arguments 是 [Person, "张三", 20],shift后 constructor = Person,arguments 变成 ["张三", 20]
  //.call 的核心目的是 「让类数组 arguments 借用数组的 shift 方法」
  // 2. 新建空对象,原型绑定到构造函数的 prototype(原理第1+2步)
  const newObject = Object.create(constructor.prototype);
  // 等价于:const newObject = {}; newObject.__proto__ = constructor.prototype;(但 Object.create 更标准)
  // 3. 绑定 this 为 newObject,执行构造函数(原理第3步)
  const result = constructor.apply(newObject, arguments);
  // 解释:arguments 此时已经是去掉第一个参数后的「初始化参数」,比如 ["张三", 20]
  // 执行后,newObject 就有了构造函数里的属性(this.name、this.age)
  // 4. 判断构造函数的返回值(原理第4步)
  // 引用类型:对象/函数 → 返回 result;值类型:返回 newObject
  const isReferenceType =
    result && (typeof result === 'object' || typeof result === 'function');
  return isReferenceType ? result : newObject;
}
function Person(name, age) {
  this.name = name;
  this.age = age;
}
// 原生 new
const p1 = new Person("张三", 20);
// 手写 objectFactory
const p2 = objectFactory(Person, "张三", 20);

console.log(p1); // Person { name: '张三', age: 20 }
console.log(p2); // Person { name: '张三', age: 20 }(和p1一致)
console.log(p1 instanceof Person); // true
console.log(p2 instanceof Person); // true(原型绑定成功)