前言
我们在深入浅出面向对象和原型【概念篇2】在这篇文章中了解到了如何使用new Function解决重复创建浪费内存的问题,其中的关键就是new,那么这篇文章让我们来重新了解new的前世今生
一个苦逼年级主任的故事
开学啦~~~高一年级主任龚主任需要为全年级每一位理科班新生录入学号并为每一位学生生成相关档案
不仅要自己留一份而且要把每一个档案都上传到学校资料库
哇,全年级一千个学生,一个个输入,不要命啦?
还好龚主任学过编程
// 先造一个对象,把相关数据都先写进去,看看是啥样的
var 学生 = {
学号: 1,
年级: '高一',
所选方向: '理科班',
上传资料: function () {/*上传资料的代码*/
}
}
// 不错,档案大致就是如此
// 再来个数组自己留着作为备份
// 那么循环一千次吧
var 全年级学生 = []
for (var i = 0; i < 1000; i++) {
var 学生 = {
学号: i,
年级: '高一',
所选方向: '理科班',
上传资料: function () {/*上传资料的代码*/}
}
全年级学生.push(学生)
}
龚主任突然想到,他昨天晚上在才在segmentfault上看到有关于内存浪费的文章——深入浅出面向对象和原型【概念篇2】
那么他写的这个代码就是典型的内存浪费啊
每个学生除了学号不同,其它都相同,咋办呢?
哎对了,那篇文章说可以通过原型和原型链解决这个问题
那么试试吧
// 先创建一个学生原型,然后把相同的代码都放在这里
var 学生原型 = {
年级: '高一',
所选方向: '理科班',
上传资料: function () {/*上传资料的代码*/}
}
// 重新写循环代码
var 全年级学生 = []
for (var i = 0; i < 1000; i++) {
var 学生 = {
学号: i
}
// 还记得吗,每个对象自动带有__proto__属性
// 不过在这里__proto__属性的指向需要我们自己去设定
学生.__proto__ = 学生原型
全年级学生.push(学生)
}
好了,大功告成,这下内存不浪费了
但是,龚主任听说程序猿写代码都追求可读性强,他这写的太不优雅了
再改改吧
// 优雅的代码离不开封装,现在让我们来封装封装吧
function 学生(学号) {
// 我们先建立一个临时对象,把例如学号之类需要改变的属性放进去
var 临时对象 = {}
临时对象.学号 = 学号
// 再把临时对象的__proto__手工绑定到学生.原型
临时对象.__proto__ = 学生.原型
return 临时对象
}
学生.原型 = {
年级: '高一',
所选方向: '理科班',
上传资料: function () {/*上传资料的代码*/
}
}
// 好了,开始循环吧
var 学生们 = []
for (var i = 0; i < 1000; i++) {
学生们.push(学生(i))
}
好了,让我们先远离一下龚先生和他的代码,来看看到底什么是new
function 学生(学号) {
// 我们先建立一个临时对象,把例如学号之类需要改变的属性放进去
// 【new做的第一件事:帮你创立一个临时对象,临时对象通过this访问】
var 临时对象 = {}
临时对象.学号 = 学号
// 再把临时对象的__proto__手工绑定到学生原型
// 【new做的第二件事:帮你自动把__proto__绑定到学生.原型】
临时对象.__proto__ = 学生.原型
// 【new做的第三件事:帮你return临时对象】
return 临时对象
}
// 【但new只有一个要求:把学生原型改名为 学生.prototype】
学生.原型 = {
年级: '高一',
所选方向: '理科班',
上传资料: function () {/*上传资料的代码*/}
}
那么,我们用new该怎么写?so easy。new帮你做的事,你还自己做它干嘛呢?
function 学生(学号) {
// 【new做的第一件事:帮你创立一个临时对象,临时对象通过this访问】
// 所以我们不用创建临时对象了,把下面那行代码注释掉
// var 临时对象 = {}
// 把临时对象都改为this就好
this.学号 = 学号
// 再把临时对象的__proto__手工绑定到学生原型
// 【new做的第二件事:帮你自动把__proto__绑定到学生原型】
// 我们不用手动绑定了,注释掉
// 临时对象.__proto__ = 学生原型
// 【new做的第三件事:帮你return临时对象】
// 我们不用手动return了,注释掉
// return 临时对象
}
// 【但new只有一个要求:把学生原型改名为 学生.prototype】
// new 帮了我们这么多忙,按照他的意思来呗,改了!
学生.prototype = {
年级: '高一',
所选方向: '理科班',
上传资料: function () {/*上传资料的代码*/}
}
var 学生们 = []
for (var i = 0; i < 1000; i++) {
学生们.push(new 学生(i))
我的天哪,我们的代码竟然通过new减少了这么多!!
constructor属性
function test(id) {
this.id = id
}
new test(1)
console.log(test.prototype) // {constructor: ƒ}
使用new操作符的时候,为了记录临时对象是由哪个函数创建的,会在prototype里添加一个constructor属性,指向创建临时对象的函数
注意:如果直接给prototype赋值,则constructor属性会消失
function 学生(学号) {
this.学号 = 学号
}
学生.prototype = {
年级: '高一',
所选方向: '理科班',
上传资料: function () {/*上传资料的代码*/}
}
var 学生们 = []
for (var i = 0; i < 1000; i++) {
学生们.push(new 学生(i))
}
// 没有出现constructor属性
console.log(学生.prototype) // {年级: "高一", 所选方向: "理科班", 上传资料: ƒ}
可以采用另一种赋值方式
function 学生(学号) {
this.学号 = 学号
}
学生.prototype.年级 = '高一'
学生.prototype.所选方向 = '理科班'
学生.prototype.上传资料 = function () {/*上传资料的代码*/}
var 学生们 = []
for (var i = 0; i < 1000; i++) {
学生们.push(new 学生(i))
}
// 出现constructor属性
console.log(学生.prototype) // {年级: "高一", 所选方向: "理科班", 上传资料: ƒ, constructor: ƒ}
总结
new的本质
new的本质其实就是一个语法糖,目的就是为了帮我们省代码
new的作用
- 创立一个临时对象,临时对象指向类的this
- 把实例__proto__绑定到类的prototype
- return临时对象(也就是this)
关于new的语法糖
var a = {} 是 var a = new Object()的语法糖
var a = [] 是 var a = new Array()的语法糖
var a = funciton(){} 是 var a = new Function()的语法糖
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。