1、new一个对象的过程(原文地址)
首先来看一下new 一个对象输出什么?
function Person(name) {
this.name = name
}
Person.prototype.sayName = function() {
console.log(this.name)
}
const p = new Person('yiyiyi')
console.log(p)
可以看到有name和[[Prototype]] (即__proto__),[[Prototype]]里面有原型方法sayName,constructor和[[Prototype]] 在输出看一下Person.prototype:
可以看出p的[[Prototype]]的指向和构造函数Person的prototype的指向相同。 因此new操作符可以分为四步:
- 创建一个空对象
- 将所创建的对象的__proto__属性值设置为构造函数的prototype的属性值
- 执行构造函数中的代码,构造函数中的this指向该对象
- 返回对象 因此上面的过程就可以等同于下面的过程:
function Person(name) {
this.name = name
}
Person.prototype.sayName = function() {
console.log(this.name)
}
const p = {}
p.__proto__ = Person.prototype
Person.call(p, 'yiyiyi')
console.log(p)
2、手动实现一个new操作符(原文地址)
function realizeNew() {
// 创建一个空对象
let obj = {}
// 获得构造函数
let Con = [].shift.call(arguments)
// 链接到原型(给obj这个新生对象的原型指向它的构造函数的原型)
obj.__proto__ = Con.prototype
// 绑定this
let result = Con.apply(obj, arguments)
// 确保new出来的是一个对象
return typeof result === 'object' ? result : obj
}
这个realize()函数需要传入的参数是:构造函数 + 属性 首先,创建一个新对象,然后通过 arguments 类数组可以知道参数中包含了构造函数以及传入的其他参数,接下来就是要想如何得到这个构造函数和其他的参数,由于 arguments 是类数组,没有直接的方法可以供其使用,可以使用以下两种方法
- Array.from(arguments).shift() 转换为数组,使用数组的方法shift取到第一项
- [].shift.call(arguments) 通过call() 让arguments能够借用shift方法
绑定this时需要注意:
- 给构造函数传入属性,注意构造函数的this属性
- 参数传进Con对obj的属性赋值, this要指向obj对象
- 在Con内部手动指定函数执行时的this使用call、apply实现
最后需要返回一个对象 测试一下
function Person(name) {
this.name = name
}
Person.prototype.sayName = function() {
console.log(this.name)
}
let p1 = new Person('yiyiyi')
console.log(p1.name) // yiyiyi
p1.sayName() // yiyiyi
let p2 = realizeNew(Person, 'yiyiyi')
console.log(p2.name) // yiyiyi
p2.sayName() // yiyiyi