假设现在要创建一个动物大象,它所拥有的属性大概如下图:
则创建一个大象的代码如下:
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 关键字,那么可以少做四件事情:
-
不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);
-
不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype);
-
不用 return 临时对象,因为 new 会帮你做;
-
不要给原型想名字了,因为 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做了的事就是:
-
创建临时对象/新对象
-
绑定原型
-
指定 this = 临时对象
-
执行构造函数
-
返回临时对象