小总结【JS的new做了什么】

102 阅读3分钟

假设现在要创建一个动物大象,它所拥有的属性大概如下图:

image.png

则创建一个大象的代码如下:

let 大象 = {
    id:1,
    种类:'生物'
    生命值:100,
    行走:function(){},
    奔跑:function(){},
    死亡:function(){},
    进食:function(){}
}
//调用一个名为"制造"的函数去创建一个大象
制造(大象)

如果此时需求变为制造100个大象

最粗暴的方式是循环100次:

let 大象们 = []
let 大象
for(let i=0; i<100; i++){
  大象 = {
    id: i, 
    生命值:100, 
    种类:'生物'
    行走:function(){},
    奔跑:function(){},
    死亡:function(){},
    进食:function(){}
  }
  大象们.push(大象)
}

制造(大象们)

问题

以上代码存在浪费内存的问题。

行走、奔跑、死亡、进食这四个动作对每一个大象来说都是一样的,只需引用同一个函数即可,没必要各创建100个。只有id需要创建100次,因为id是唯一的。

改进

用原型链可以解决重复创建的问题:先创建一个【大象原型】,然后让【大象】的 __proto__ 指向【大象原型】

let 大象原型 = {
    生命值:100
    种类:'生物'
    行走:function(){},
    奔跑:function(){},
    死亡:function(){},
    进食:function(){}
}
let 大象们 = []
let 大象
for(let i=0; i<100; i++){
  大象 = {
    id: i,
  }

  //实际工作中不要这样写,因为 __proto__ 不是标准属性
  大象.__proto__ = 大象原型 

  大象们.push(大象)
}

制造(大象们)

再改进

有人认为这样的代码不够优雅,于是我们用一个函数把这两部分联系起来:

function 大象(id){
  let 临时对象 = {}

  临时对象.__proto__ = 大象.原型

  临时对象.id = id
  return 临时对象
}

大象.原型 = {
    生命值:100
    种类:'生物'
    行走:function(){},
    奔跑:function(){},
    死亡:function(){},
    进食:function(){}
}

// 保存为文件:大象.js

然后创建大象们:

let 大象们 = []
for(let i=0; i<100; i++){
  大象们.push(大象(i))
}

制造(大象们)

有请 new 登场

以上的代码就实现了我们的需求,而new则帮我们完成了一部分代码。

只要你在大象前面使用 new 关键字,那么可以少做四件事情:

  1. 不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);

  2. 不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype);

  3. 不用 return 临时对象,因为 new 会帮你做;

  4. 不要给原型想名字了,因为 new 指定名字为 prototype。

用new再把代码实现一次

function 大象(id){
  this.id = id
}

大象.prototype = {
    生命值:100
    种类:'生物'
    行走:function(){},
    奔跑:function(){},
    死亡:function(){},
    进食:function(){}
}

// 保存为文件:大象.js

然后是创建大象们:

let 大象们 = []
for(let i=0; i<100; i++){
  大象们.push(new 大象(i))
}

制造(大象)

所以new做了的事就是:

  1. 创建临时对象/新对象

  2. 绑定原型

  3. 指定 this = 临时对象

  4. 执行构造函数

  5. 返回临时对象