手写 JS 中的 new

138 阅读2分钟

开始

1. new 是什么

new 操作符是可以创建一个用户定义的对象的实例或具有构造函数的内置对象的实例

下面看一个例子
function Car (make, model, year) {
    this.make = make
    this.model = model
    this.year = year
}

Car.prototype.running = function () {
    return `${this.year}${this.make} 公司造的 ${this.model} 牌汽车,开动起来`
}

const car = new Car('长城', '坦克300', '2022')
console.log(car)

打印出的 car 实例

1679468118753.jpg

从上图中可以看到,实例里面有以下内容:

  • 三个属性:make, model, year 并且已经赋值
  • 原型上有一个 running 方法和一个构造器函数 Car
思考一下, 如果构造函数返回了一个新对象或者返回其它基本类型的数据,结果还一样吗
修改上面的例子,使构造函数返回一个新对象
function Car (make, model, year) {
    this.make = make
    this.model = model
    this.year = year
    return {
        info: `${this.year}${this.make} 公司造的 ${this.model}` 
    }
}

Car.prototype.running = function () {
    return `${this.year}${this.make} 公司造的 ${this.model} 牌汽车,开动起来`
}

const car = new Car('长城', '坦克300', '2022')
console.log(car)

打印出的 car 实例

1679469950742.jpg

从上图中可以看出:

  • 实例是个普通的 object 对象,这个对象就是执行构造函数 return 时的结果
  • 实例的原型是 Object
继续修改上面的例子,使其构造函数返回一个基本类型的数据‘测试’
function Car (make, model, year) {
    this.make = make
    this.model = model
    this.year = year
    return '测试'
}

Car.prototype.running = function () {
    return `${this.year}${this.make} 公司造的 ${this.model} 牌汽车,开动起来`
}

const car = new Car('长城', '坦克300', '2022')
console.log(car)

打印出的 car 实例

1679468118753.jpg

从上图中可以看出和没有写 return 是一样的,返回的都是新创建的 Car 实例

2. 执行 new 会发生什么

根据上面的例子内容,可以总结出使用 new 会进行如下的操作:

  • 创建一个空的简单 js 对象(即{})
  • 为步骤 1 新创建的对象上面添加属性 proto, 并将该属性链接至构造函数的原型对象
  • 将步骤 1 新创建的对象作为 this 的上下文
  • 如果该函数没有返回对象,则返回 this

3. 实现 new

知道了 new 的执行过程,手写一个 new ,就变得很容易了

方法一 代码如下:

// construct: 构造函数 rest: 参数
function newApply (construct, ...rest) {
    // 步骤一 创建一个空对象
    const newObj = {} 
    // 步骤二 新创建的对象上面添加属性 __proto__, 并将该属性链接至构造函数的原型对象
    newObj.__proto__ = construct.prototype 
    // 步骤三 新创建的对象作为 this 的上下文
    const result = construct.apply(newObj, rest)
     // 步骤四 如果执行结果有返回值并且是一个对象,就返回执行的结果,否者返回 this 也就是新创建的对象 newObj
    return typeof result === 'object' ? result : newObj
}

方法二 代码如下:

// construct: 构造函数 rest: 参数
function newCall (construct, ...rest) {
    // 步骤一 基于 construct 的原型创建一个新的对象
    const newObj = Object.create(construct.prototype )
    // 步骤二 新创建的对象作为 this 的上下文
    const result = construct.call(newObj, ...rest)
     // 步骤三 如果执行结果有返回值并且是一个对象,就返回执行的结果,否者返回 this 也就是新创建的对象 newObj
    return typeof result === 'object' ? result : newObj
}
测试一下 方法一
const car = new Car('长城', '坦克300', '2022')
console.log('car', car)
const newApplyCar = newApply(Car, '长城', '坦克300', '2022')
console.log('newApplyCar', newApplyCar)

打印一下,发现car, 和newApplyCar 完全一致

1679472497113.jpg

测试一下 方法二
const car = new Car('长城', '坦克300', '2022')
console.log('car', car)
const newApplyCar = newCall(Car, '长城', '坦克300', '2022')
console.log('newApplyCar', newApplyCar)

打印一下,发现car, 和newApplyCar 也完全一致 1679472497113.jpg

成功实现了手写 new 了 哈哈哈

结束