手写new
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实例
从上图可以看到,实例里面有以下内容:
- 三个属性:make,model,year并且已经赋值
- 原型上有一个running方法,constructor为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实例
从上图中可以看出:
- 实例是个普通的Object对象,这个对象就是执行构造函数return时的结果
- 实例的原型是Object,且其constructor不再是Car,而是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实例
从上图中可以看出和没有写return是一样的,返回的都是新创新的Car实例
执行new会发生什么
根据上面的例子内容,可以总结出使用new会进行如下的操作:
- 创建一个空的简单js对象(即{})
- 为该对象设置原型,将对象的原型(proto)设置为构造函数的protortype对象
- 将函数的this指向这个对象,执行构造函数,并将参数进行赋值。
- 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
实现new
知道了new的执行过程,手写一个new,就变得很容易了
- 方法1
function newApply (Fun, ...rest) {
// 步骤一 创建一个空对象
const newObj = {}
// 步骤二 新创建的对象上面添加属性 __proto__, 并将该属性链接至构造函数的原型对象
newObj.__proto__ = Fun.prototype
// 步骤三 新创建的对象作为 this 的上下文
const result = Fun.apply(newObj, rest)
// 步骤四 如果执行结果有返回值并且是一个对象,就返回执行的结果,否者返回 this 也就是新创建的对象 newObj
return result instanceof Object ? result : newObj
}
测试
const car = new Car('长城', '坦克300', '2022')
console.log('car', car)
const newApplyCar = newApply(Car, '长城', '坦克300', '2022')
console.log('newApplyCar', newApplyCar)
- 方法2
function newCall (Fun, ...rest) {
// 步骤一 基于 Fun 的原型创建一个新的对象。不管怎么样都不建议修改对象的原型,这可能会极大的影响性能。因此原型赋值这块代码可以优化下,直接使用Object.create,在创建的时候就设置好原型
const newObj = Object.create(Fun.prototype)
// 步骤二 新创建的对象作为 this 的上下文
const result = Fun.call(newObj, ...rest)
// 步骤三 如果执行结果有返回值并且是一个对象,就返回执行的结果,否者返回 this 也就是新创建的对象 newObj
return result instanceof Object ? result : newObj
}
测试
const car = new Car('长城', '坦克300', '2022')
console.log('car', car)
const newApplyCar = newCall(Car, '长城', '坦克300', '2022')
console.log('newApplyCar', newApplyCar)