1.2.4、new一个对象的详细过程,手动实现一个 new操作符(JavaScript基础-原型和原型链)

252 阅读2分钟

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)

new Person输出.png

可以看到有name和[[Prototype]] (即__proto__),[[Prototype]]里面有原型方法sayName,constructor和[[Prototype]] 在输出看一下Person.prototype:

Snipaste_2022-05-16_15-25-36.png

可以看出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