最近在准备面试,经典题目之一,想写篇博客记录清楚,JS 的 new 做了什么?参考了方应杭的这篇文章
new 做了五件事
- 创建了一个临时对象
- 绑定原型
- 指定 this = 临时对象
- 执行构造函数
- 返回该临时对象
使用 js 原生代码实现new
function myNew () {
//创建一个新对象
var obj = new Object ();
//取出参数中的第一个参数,获得构造函数
var constructor = Array.prototype.shift.call(arguments);
//连接原型,新对象可以访问原型中的属性
obj._proto_ = constructor.prototype;
// 执行构造函数,即绑定 this,并且为这个新对象添加属性
var result = constructor.apply(obj,arguments);
return typeof result === "object" ? result :obj ;
}
更简单的写法就是
function myNew(fn) {
const obj = Object.create(fn.prototype);
result = fn.apply(obj, [...arguments].slice(1));
return typeof result === "object" ? result : obj;
}
// var obj = Object.create(fn.prototype) 等价于:
// var obj = new Object ();
// obj._proto_ = constructor.prototype;
底层逻辑
那里面的底层逻辑是什么呢,方方的文章说的很清楚。
若我们要制造一个士兵,如下图:
代码其实很简单
var 士兵 = {
ID: 1, // 用于区分每个士兵
兵种:"美国大兵",
攻击力:5,
生命值:42,
行走:function(){ /*走俩步的代码*/},
奔跑:function(){ /*狂奔的代码*/ },
死亡:function(){ /*Go die*/ },
攻击:function(){ /*糊他熊脸*/ },
防御:function(){ /*护脸*/ }
}
兵营.制造(士兵)
那要批量制造的话,我们就会想到循环
var 士兵们 = []
var 士兵
for(var i=0; i<100; i++){
士兵 = {
ID: i, // ID 不能重复
兵种:"美国大兵",
攻击力:5,
生命值:42,
行走:function(){ /*走俩步的代码*/},
奔跑:function(){ /*狂奔的代码*/ },
死亡:function(){ /*Go die*/ },
攻击:function(){ /*糊他熊脸*/ },
防御:function(){ /*护脸*/ }
}
士兵们.push(士兵)
}
兵营.批量制造(士兵们)
但是,这就会浪费很多内存。一样的操作我们重复创建了很多次,这里面只有 ID 和生命值不一样。
简言之就是利用原型,那就直接把共有属性放在原型上面就好了,用原型链可以解决重复创建的问题:我们先创建一个「士兵原型」,然后让「士兵」的__proto__指向「士兵原型」
function 士兵(ID){
var 临时对象 = {}
临时对象.__proto__ = 士兵.原型
临时对象.ID = ID
临时对象.生命值 = 42
return 临时对象
}
士兵.原型 = {
兵种:"美国大兵",
攻击力:5,
行走:function(){ /*走俩步的代码*/},
奔跑:function(){ /*狂奔的代码*/ },
死亡:function(){ /*Go die*/ },
攻击:function(){ /*糊他熊脸*/ },
防御:function(){ /*护脸*/ }
}
// 保存为文件:士兵.js
然后就可以愉快地引用「士兵」来创建士兵了:
var 士兵们 = []
for(var i=0; i<100; i++){
士兵们.push(士兵(i))
}
兵营.批量制造(士兵们)
new 关键字的诞生
JS 之父创建了 new 关键字,可以让我们少写几行代码:
只要你在士兵前面使用 new 关键字,那么可以少做四件事情:
-
不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);
-
不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype);
-
不用 return 临时对象,因为 new 会帮你做;
-
不要给原型想名字了,因为 new 指定名字为 prototype。
这一次我们用 new 来写
function 士兵(ID){
this.ID = ID
this.生命值 = 42
}
士兵.prototype = {
兵种:"美国大兵",
攻击力:5,
行走:function(){ /*走俩步的代码*/},
奔跑:function(){ /*狂奔的代码*/ },
死亡:function(){ /*Go die*/ },
攻击:function(){ /*糊他熊脸*/ },
防御:function(){ /*护脸*/ }
}
// 保存为文件:士兵.js
然后是创建士兵(加了一个 new 关键字):
var 士兵们 = []
for(var i=0; i<100; i++){
士兵们.push(new 士兵(i))
}
兵营.批量制造(士兵们)
new 的作用就是省几行代码——所谓的语法糖