解释一下 JS 里面的 new 都干了啥

154 阅读1分钟

总述

根据MDN的解释,new 关键字会进行如下的操作:

  1. 创建一个空的简单 JavaScript 对象(即 {} );
  2. 为步骤 1 新创建的对象添加属性 __proto__ ,将该属性链接至构造函数的原型对象 ;
  3. 将步骤 1 新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回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 👩🏻‍💻|👨🏻‍💻!