一文帮你搞定new 操作符运行原理

204 阅读3分钟

前言

在JavaScript中,new操作符是一个特殊的操作符,用于创建并返回一个由构造函数定义的新对象。它的工作原理大致如下:

  1. 创建一个新的空对象。
  2. 将这个空对象的原型([[Prototype]])设置为构造函数的prototype属性。
  3. 将这个空对象作为this的上下文绑定到构造函数并调用构造函数。
  4. 如果构造函数返回了一个对象,那么这个对象会被返回;如果没有,则返回步骤1中创建的对象。

new 关键字方法执行流程图

stateDiagram-v2 
Start --> 创建newObject对象
Start --> 获取constructor构造函数
Start --> 创建result与flag处理返回值
创建newObject对象 --> 判断constructor是不是函数
获取constructor构造函数 --> 判断constructor是不是函数 
创建result与flag处理返回值 --> 判断constructor是不是函数 
判断constructor是不是函数 --> 非函数类型抛出异常
非函数类型抛出异常 --> 结束
判断constructor是不是函数 --> 函数类型
函数类型 --> 为newObject对象赋值新创建的对象
函数类型 --> 为result赋值constructor返回内容
为newObject对象赋值新创建的对象 --> 判断result是否有返回值
为result赋值constructor返回内容 --> 判断result是否有返回值
判断result是否有返回值 --> 无返回
无返回 --> 返回newObject对象
判断result是否有返回值 --> 返回基础类型
返回基础类型 --> 返回newObject对象
判断result是否有返回值 --> 有返回
有返回 --> 返回result
返回result --> 结束
返回newObject对象 --> 结束

代码仿写

总览

function g_New() {

  let newObject = null;
  let constructor = Array.prototype.shift.call(arguments); // [].shift.call(arguments)
  let result = null, flag = false;
  
  if (typeof constructor !== "function") {
    throw new TypeError("constructor must be a function");
  }

  newObject = Object.create(constructor.prototype);
  result = constructor.apply(newObject, arguments);
  flag = (result && typeof result === "object") || typeof result === "function";

  return flag ? result : newObject;
}
 

疑难解析

有些同学可能会对代码产生一些疑问例如:

  1. 第4行为什么要 通过call改变this指向?
  2. 第7行为什么要判断constructors是不是函数类型?
  3. 第11行与12行为什么要创建一个新对象并改变this指向为新创建的对象?
  4. 第13与15行为什么要进行三元判断?

下面便让我们来解答一下相关疑问

  • 第4行为什么要 通过call改变this指向
 let constructor = Array.prototype.shift.call(arguments);

解析:这段代码将arguments当做数组对象处理,但arguments并非是一个真正的数组对象,它是一个类数组对象,它包含了函数接收到的所有参数,通过call方法改变shift方法this指向,取出arguments对象中对应的数组,截取第一个元素并进行返回赋值给constructor。我们可以将该行代码改写为

let constructor =  [].shift.call(arguments)

之所以没采用是因为 [].shift会创建数组实例,在性能消耗上要比直接使用Array.prototype.shift要大因此未进行改写

  • 第7行为什么要判断constructors是不是函数类型
if (typeof constructor !== "function") {
  throw new TypeError("constructor must be a function");
}

解析:我们后续操作是创建新的对象并进行返回,若constructor不是函数则后续的Object.createconstructor.apply将无意义,并会进行报错

  • 第11行与12行为什么要创建一个新对象并改变this指向为新创建的对象?
newObject = Object.create(constructor.prototype);
result = constructor.apply(newObject, arguments);

解析:第11行代码是为了给新创建的对象共享当前上下文原型链上所有属性与方法,调用apply方法是为了将构造函数内部的this指向新创建的newObject对象,这样构造函数就可以为newObject对象添加属性与方法,并使用result来接收constructor的调用结果

  • 第13与15行为什么要进行三元判断
let flag = (result && typeof result === "object") || typeof result === "function";
return flag ? result : newObject;

解析:通常constructor不会返回任何结果,若是返回基本类型也会被忽略,直接返回新创建的newObject对象,若constructor返回为对象则直接使用,返回对应的结果,后续若有对应的操作可以进行续写。