# 1.前言

众所周知:没有对象怎么办?那就 new 一个!

那么在 JS 中,当我们 new 一个对象的时候,这个 new 关键字内部都干了什么呢? 现在我们就来剖析一下原生 JS 中 new 关键字内部的工作原理。

# 2.原生的 new

首先,我们先 new 一个对象看看:

//创建Person构造函数,参数为name,age
function Person(name, age) {
  this.name = name;
  this.age = age;
}
//实例化对象小明
xm = new Person("xiaoming", 18);
//打印实例化出来的对象小明
console.log(xm);
1
2
3
4
5
6
7
8
9

打印结果:

从打印结果中可以看到:

用 new 关键字实例化对象时,首先创建了一个空对象 xm,并且这个空对象包含两个属性 name 和 age,分别对应构造函数中的两个属性,其次我们也可以知道实例化出来的这个对象 xm 是继承自 Person.prototype。

# 3.new 命令的原理

了解了以上过程,那么现在我们就可以总结出 new 关键字在实例化对象时内部都干了什么,其实,new 关键字内部干了如下四件事:

  1. 创建一个空对象,作为将要返回的对象实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。
  3. 执行构造函数,并将这个空对象赋值给函数内部的this关键字。
  4. 判断构造函数的返回值,如果是对象则返回这个对象,否则返回新创建的对象。

也就是说,构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子。

如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。

# 4.封装_new 函数

当我们知道 new 关键字的内部原理后,我们就可以封装一个_new 函数,使其用于与 new 关键字同样的功能。

_new 函数需要传入以下几个参数: 第一个参数:构造函数名 Func; 第二个参数及后面的参数:构造函数的参数

function _new() {
  //1.拿到传入的参数中的第一个参数,即构造函数名Func
  var Func = [].shift.call(arguments); //2.创建一个空对象obj,并让其继承Func.prototype
  var obj = Object.create(Func.prototype); //3.执行构造函数,并将this指向创建的空对象obj
  var result = Func.apply(obj, arguments); //4.如果构造函数返回结果是对象,就直接返回,否则返回创建的对象obj
  return typeof result === "object" && result != null ? result : obj;
}
1
2
3
4
5
6
7

# 5.测试

封装好后,我们来测试一下封装的_new 函数,看看它是否实现了和原生 new 关键字同样的功能。

//创建Person构造函数,参数为name,age
function Person(name, age) {
  this.name = name;
  this.age = age;
}

function _new() {
  //1.拿到传入的参数中的第一个参数,即构造函数名Func
  var Func = [].shift.call(arguments); //2.创建一个空对象obj,并让其继承Func.prototype
  var obj = Object.create(Func.prototype); //3.执行构造函数,并将this指向创建的空对象obj
  var result = Func.apply(obj, arguments); //4.如果构造函数返回结果是对象,就直接返回,否则返回创建的对象obj
  return typeof result === "object" && result != null ? result : obj;
}

xm = _new(Person, "xiaoming", 18);

console.log(xm);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

测试结果:

从测试结果看到,_new 函数的功能与 new 关键字完全一致。