前言
大家好 , 我是一名在校大二学生 ~
今天考完六级后 , 决定深入搞搞 new 操作符, 并使用函数实现一遍 new .
请倔友往下看 ~
明白 new 的底层
首先要明白 , new 的三个问题
- new 是什么 ?
- new 作用于谁 ?
- new 之后 , 底层发生了什么 ?
只有搞懂了这些 , 我们才能自主实现一个和 new 功能一样的函数 ,
我总结出下面这段话 , 回答前两个问题 :
new 是 javaScript 的操作符 , 通过作用于某个构造函数 , 创建该构造函数的实例对象
有两个关注点
- new 是作用于构造函数的
- 对构造函数使用后 , 返回该构造函数的实例对象
请倔友看下面这句代码
const p = new Person(name,age)
这里的 Person() 是构造函数 , 所以 new 才能作用于它 , new 之后返回 Person() 构造函数的实例对象 , 这里使用 p 变量来引用
我们补全 构造函数的代码
首先使用适合 java 选手体质的 es6 写法 , 完整代码如下 :
class Person {
construct(name ,age){
this.name = name ;
this.age = age ;
}
speak(){
console.log(`我的名字叫${this.name},今年${this.age}岁`)
}
}
const name = 'ganzhibin';
const age = 19 ;
const p = new Person(name,age)
再使用便于考古的 es5 写法 :
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.speak = function () {
console.log(`我的名字叫${this.name},今年${this.age}岁`)
}
const name = 'ganzhibin';
const age = 19 ;
const p = new Person(name,age)
在 es5 中 ,我们发现 : 方法需要手动添加到构造函数的 prototype 对象上
其实 , es6 的构造函数方法是 es5 的语法糖 , 自动将方法添加到类的原型对象上,因此可以被所有实例共享 。
说实话 ,这颗糖真甜 ~ 🤡
现在只剩下最后一个问题了
new 之后 , 底层发生了什么 ?
下面是一段使用 new 关键字的代码示例:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
console.log(this.name);
}
const p1 = new Person('ganzhibin', 20)
在这个例子中,我们创建了一个构造函数Person,当我们调用 new Person('ganzhibin', 20) 时,发生了以下步骤:
- 创建新对象:JavaScript 引擎首先创建了一个全新的空对象
{}。- 设置原型链:接着,该新对象的内部属性
[[Prototype]](可通过__proto__访问)被设置为Person.prototype,建立了新对象与构造函数之间的原型链连接。- 绑定
this:然后,构造函数内的this被绑定到了新创建的对象上,使得我们可以在构造函数内部对新对象进行属性和方法的添加。- 返回新对象:最后,如果构造函数没有显式返回另一个对象,则默认返回新创建的对象。
手写 new 函数
经过上面的铺垫 ,要实现一个 new 函数
首选确定函数的参数是什么 ?
const p = new Person(name,age)
仔细看上面的代码 , 我们发现 , 需要一个构造函数作为参数 , 还需要初始化对象的参数 , 而这个参数是可变的 , 上面只是初始化了 name , age , 可能将来还要实现初始化 gender
所以可以这样写 :
function myNew(constructor , ...args){
//constructor 构造函数
// ... args args为可变参数
}
而在使用 new 操作符后 , 底层的机制是 :
- 创建新对象:JavaScript 引擎首先创建了一个全新的空对象
{}。- 设置原型链:接着,该新对象的内部属性
[[Prototype]](可通过__proto__访问)被设置为Person.prototype,建立了新对象与构造函数之间的原型链连接。- 绑定
this:然后,构造函数内的this被绑定到了新创建的对象上,使得我们可以在构造函数内部对新对象进行属性和方法的添加。- 返回新对象:最后,如果构造函数没有显式返回另一个对象,则默认返回新创建的对象。
所以我们按照 new 的底层开始写代码 :
function myNew(constructor , ...args) {
// 1. 创建一个简单的js 空对象
const obj = {};
// 2. 将空对象的原型指向构造函数的原型
obj.__proto__ = constructor.prototype;
//或者这样写 : Object.setPrototypeOf(obj, constructor.prototype)
// 3. 将这个[空对象]作为this的[上下文执行构造函数] , 即this 指向新创建的空对象
const result = constructor.apply(obj, args)
// 4. 如果构造函数返回一个对象 , 则返回该对象 ; 否则返回这个新创建的对象
return result != null && (typeof result === 'object' || typeof result === 'function') ? result : obj;
//以上return注意 : typeof null === 'object' , 所以要判断
//typeof result === 'object' || typeof result === 'function'
//构造函数可能会出现返回一个函数
}
测试
// 5. 测试
//构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
const person = myNew(Person, "ganzhibin", 19);
console.log(person) // { name: 'ganzhibin', age: 19 }
console.log(person instanceof Person) // true
使用 new 的话也是这个结果
function Person(name, age) {
this.name = name;
this.age = age;
}
const person = new Person( "ganzhibin", 19);
console.log(person) // { name: 'ganzhibin', age: 19 }
console.log(person instanceof Person) // true
总结
重点明白以下问题 , 实现 new 函数代码就可以用手了 ~ 🤡
- new 是什么 ?
- new 作用于谁 ?
- new 之后 , 底层发生了什么 ?
祝我们每天都充满 Passion , 欢迎大家 , 一起交流 ~