深耕系列之new关键字

125 阅读2分钟

前面文章讲到深耕系列之this指向,提到bind方法的代码实现中,要支持new方式。

那么这篇文章会去深入理解new运算符,理解它都做了哪些操作。

定义

一句话,new运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

我们先来看一个例子:

function Car(make, model, year) {
  this.make = make
  this.model = model
  this.year = year
}
Car.prototype.color = 'red'

const car1 = new Car('Eagle', 'Talon TSi', 1993)

console.log(car1.make)
console.log(car1.color)

粘贴到控制台上打印,发现相关结果如下:

console.log(car1.make)  // Eagle
console.log(car1.color)   // red

可以看出,car1实例可以访问到Car函数中的属性和Car.prototype中的属性,为什么会这样呢,new运算符做了什么哪些操作?

new运算符模拟实现

分析

经过查资料,我们发现new关键字会进行如下的操作:

  1. 创建一个空对象,即{}
  2. 给第一步创建的对象添加__proto__属性,将该属性的值设为构造函数的原型对象,也就是car1.proto = Car.prototype
  3. 将第一步创建的对象设为this的上下文,所以在Car函数内,执行this.make等代码时,就相当于给实例对象赋值
  4. 如果该函数没有返回对象,就返回this,也就是第一步创建的对象.(如果该函数返回的不是对象,那么按没有返回值来处理)

实现

由于new是一个关键字,我们不能直接像bind、apply那样直接覆盖,故使用一个函数模拟myNew,如:

function Car(make, model, year) {
  this.make = make
  this.model = model
  this.year = year
}
Car.prototype.color = 'red'

const car1 = new Car('Eagle', 'Talon TSi', 1993)
const car2 = myNew(Car, 'Eagle', 'Talon TSi', 1993)

希望car1和car2这两种创建出来的对象,内容是一致的

那么实现的思路是:

  1. 创建一个空的新对象
  2. 通过arguments.shift()获取构造函数
  3. 把构造函数的prototype属性赋值给新对象的__proto__属性
  4. 使用apply函数来设置构造函数的上下文是新对象
  5. 判断返回值是否为对象,如果为对象,就返回对象,否则返回第一步创建的新对象

实现代码如下:

function myNew() {
	let obj = {}
	let Constructor = arguments.shift()
	obj.__proto__ = Constructor.prototype
	let ret = Constructor.apply(obj, arguments)
	
	return typeof ret === 'object' ? ret : obj
}

总结

以上就是我对new运算符的理解。如果觉得对你有帮助,请帮忙点个赞+评论+收藏,关注不失联。