破除迷津,来了解如何实现JavaScript 中的new

473 阅读3分钟

前言

大家好,我是抹茶。

在JavaScript中,new是一个关键字,它后面跟着函数调用的话,默认情况会返回一个新的对象,这种情况被称为构造函数调用。那么,我们如何自己实现一个new方法呢?

设计与思考

首先我们要思考,new都具备了哪些功能?

  • 它会返回一个对象
  • 对象的原型链指向的是构造函数的prototype
  • 对象会被绑定到函数调用的this

编码实现

第一步,会返回一个对象,所以创造一个新的对象

function myNew (...rest) {
  // 1.创建一个新的对象
  const obj = {};

 return obj;
}

第二步,new 后面跟着的是一个函数,所以要获取构造函数

function myNew (...rest) {
  // 1.创建一个新的对象
  const obj = {};

  // 2.获取构造函数
  const Con = rest.shift(); // 构造函数是第一个参数

  return  obj;
}

第三步,将obj的原型链接到构造函数的原型上

function myNew (...rest) {
  // 1.创建一个新的对象
  const obj = {};

  // 2.获取构造函数
  const Con = rest.shift(); // 函数是第一个参数

  // 3.将对象的原型链接到构造函数的prototype上
  Object.setPrototypeOf(obj, Con.prototype);

 
  return  obj;
}

第四步,构造函数执行,执行时要绑定this,this指向的是预期返回的对象

function myNew (...rest) {
  // 1.创建一个新的对象
  const obj = {};

  // 2.获取构造函数
  const Con = rest.shift(); // 函数是第一个参数

  // 3.将对象的原型链接到构造函数的prototype上
  Object.setPrototypeOf(obj, Con.prototype);

  // 4.构造函数执行,其this要绑定我们新创建的obj
  const result = Con.apply(obj, rest);

 return obj;
}

第五步,判断选择返回的对象,因为是返回的对象,所以需要判断构造函数的执行返回的是否是对象

function myNew (...rest) {
  // 1.创建一个新的对象
  const obj = {};

  // 2.获取构造函数
  const Con = rest.shift(); // 函数是第一个参数

  // 3.将对象的原型链接到构造函数的prototype上
  Object.setPrototypeOf(obj, Con.prototype);

  // 4.构造函数执行,其this要绑定我们新创建的obj
  const result = Con.apply(obj, rest);

  // 5.判断返回对象
  return typeof result == 'object' && result !== null ? result: obj;
}

现在,我们自己设计实现的myNew已经大功告成,接下来测试一下。

测试功能

思考一下我们预期的结果是怎样的,首先,new的构造调用会返回一个对象,其次,这个对象的原型会链接到构造函数的prototype
我们采用的测试代码如下:

function myNew (...rest) {
  // 1.创建一个新的对象
  const obj = {};

  // 2.获取构造函数
  const Con = rest.shift(); // 函数是第一个参数

  // 3.将对象的原型链接到构造函数的prototype上
  Object.setPrototypeOf(obj, Con.prototype);

  // 4.构造函数执行,其this要绑定我们新创建的obj
  const result = Con.apply(obj, rest);

  // 5.判断返回对象
  return typeof result == 'object' && result !== null ? result: obj;
}

function Con(name) {
  this.text = "hello world";
  this.num = 10086;
  this.name = name;
}

const a = myNew(Con,'抹茶');

console.log(a);// Con { text: 'hello world', num: 10086, name: '抹茶' }
console.log(Object.getPrototypeOf(a) == Con.prototype);//true

以上,我们自己实现的myNew方法通过了测试。

总结

在JavaScript中,new后面跟着一个函数调用的情况如new myFn(),叫作构造函数调用,与普通的形如myFn()不同的是,它会默认返回一个对象,这个对象的原型会链接到构造函数的prototype上。ES6中的class其实是function的语法糖,正是因为JS引擎内部通过new实现了原型链的绑定,new出来的实例才能继承父类的属性和方法(本质是通过原型链访问)。

本文详细的一步步的介绍了如何实现一个new,从而剥开这隐秘的底层设计原理。