总述
根据MDN的解释,new 关键字会进行如下的操作:
- 创建一个空的简单 JavaScript 对象(即
{}); - 为步骤 1 新创建的对象添加属性
__proto__,将该属性链接至构造函数的原型对象 ; - 将步骤 1 新创建的对象作为
this的上下文 ; - 如果该函数没有返回对象,则返回
this。
虽然MDN解释的很清晰,但是我们还是自己写一个 类似new功能的代码,考验一下自己是不是真正理解了这短短的四行字。 下面我们会创建一个 myNew 函数,这个 myNew 函数会接收一个构造函数(User),以及这个构造函数需要的参数(username,age)。最后我们会用下面的代码来对比使用 myNew 和 new 的结果:
function User(username, age){
this.username = username;
this.age = age;
this.sayHello = ()=>`Hello ${this.username}`
}
/**
* myNew:自己创造的 new
* User:构造函数
*/
const user1 = myNew(User, 'John', 44, 'teacher');
const user2 = new User('John', 44, 'Engineer');
console.log("user1", user1, user1.sayHello())
console.log("user2", user2, user2.sayHello())
步步分解
准备:首先我们先写一个基本什么都没有的 myNew。constructor 代表构造函数,...params 是es6 剩余参数的用法。
function myNew(constructor, ...params){
}
第一步:创建一个空的简单 JavaScript 对象(即 {} ).
function myNew(constructor, ...params){
// 在这里我们创建了一个空的对象init
const init = {};
}
第二步 为步骤 1 新创建的对象添加属性 __proto__ ,将该属性链接至构造函数的原型对象
function myNew(constructor, ...params){
const init = {};
// 把构造函数的原型对象传给我们新创造的空对象init
Object.setPrototypeOf(init, constructor.prototype);
}
第三步: 将步骤 1 新创建的对象作为this的上下文 ;
这一步比前两步稍微难一点。如果看不懂这一步,建议了解 js bind call 和 apply 的用法。这里我们使用apply 把构造函数User中的 this 指向 init。
function myNew(constructor, ...params){
const init = {};
Object.setPrototypeOf(init, constructor.prototype);
// [...params] 使用的是 es6 展开语法
// 通过apply 我们把构造函数中的 this 指向了 我们新建的init对象。
const res = constructor.apply(init, [...params]);
}
第四步:如果该函数没有返回对象,则返回this。
这句话MDN的中文版翻译的不完整。应该是如果没有返回对象,返回this, 但是如果有,则返回对象。
因此我们需要判断 上一步 通过apply运行构造函数的返回结果是否是对象, 也就是说判断 res 是否是对象。这里我们可以用 typeof
function myNew(constructor, ...params){
const init = {};
Object.setPrototypeOf(init, constructor.prototype);
const res = constructor.apply(init, [...params]);
if(typeof res === 'object') return res;
return init;
}
Voilà
让我们回到文章的最开始。对比一下 myNew 和 new 使用结果吧。 完整代码:
function User(username, age){
this.username = username;
this.age = age;
this.sayHello = ()=>`Hello ${this.username}`
}
function myNew(constructor, ...params){
const init = {};
Object.setPrototypeOf(init, constructor.prototype);
const res = constructor.apply(init, [...params]);
if(typeof res === 'object') return res;
return init;
}
/**
* myNew:自己创造的 new
* User:构造函数
*/
const user1 = myNew(User, 'Miaomiao', 24, 'teacher');
const user2 = new User('Miaomiao', 24, 'teacher');
console.log("user1", user1, user1.sayHello())
console.log("user2", user2, user2.sayHello())
结语
自己写了一遍代码下来,刚刚觉得,好像懂了 new 都干些啥了。希望,路人也有所收获。 Happy Coding 👩🏻💻|👨🏻💻!