菜单

JS 的 new 到底是为啥的?

2018年12月18日 - JavaScript

JS 的 new 到底是胡的?

2017/04/10 · JavaScript
· 4 评论 ·
new

初稿出处: 方应杭   

大部分讲 new
的篇章会从面向对象的思路讲起,可是自始终认为,在说一个东西的当儿,不应有引入另一个重复扑朔迷离的事物。

今己自「省代码」的角度来讲 new。

—————————

想像我们当做一个政策类战争游戏,玩家可操作一堆士兵攻击对方。

俺们要来探讨一下斯游戏里面的「创造士兵」环节。

一个新兵的当微机里便是如出一辙积聚属性,如下图:

图片 1

咱就待这么就足以制作一个战士:

JavaScript

var 士兵 = { ID: 1, // 用于区分每个士兵 兵种:”美利坚联邦合众国士兵”, 攻击力:5,
生命值:42, 行走:function(){ /*举手投足俩步的代码*/}, 奔跑:function(){
/*狂奔的代码*/ }, 死亡:function(){ /*Go die*/ }, 攻击:function(){
/*贴他熊脸*/ }, 防御:function(){ /*护脸*/ } } 兵营.制造(士兵)

1
2
3
4
5
6
7
8
9
10
11
12
13
var 士兵 = {
  ID: 1, // 用于区分每个士兵
  兵种:"美国大兵",
  攻击力:5,
  生命值:42,
  行走:function(){ /*走俩步的代码*/},
  奔跑:function(){ /*狂奔的代码*/  },
  死亡:function(){ /*Go die*/    },
  攻击:function(){ /*糊他熊脸*/   },
  防御:function(){ /*护脸*/       }
}
 
兵营.制造(士兵)

C++ new到底new什么,到底new

9: int *obj = new int(6);
00F714CE push 4 //压栈
00F714D0 call operator new (0F711EAh) //调用new函数返回分配的地址addr --- 0F711EAh是指令 jmp operator new 的地址
00F714D5 add esp,4 //恢复栈
00F714D8 mov dword ptr [ebp-0F8h],eax //把返回的地址addr赋给起始地址为ebp-0F8h的内存空间
00F714DE cmp dword ptr [ebp-0F8h],0 //判断分配是否成功
00F714E5 je main+51h (0F71501h)  //如果不成功跳往0x0F71501h   
00F714E7 mov eax,dword ptr [ebp-0F8h] //起始地址为ebp-0F8h 的内存空间(addr)赋值给eax
00F714ED mov dword ptr [eax],6 //把以起始地址为eax的内存空间(addr)置成6
00F714F3 mov ecx,dword ptr [ebp-0F8h] //起始地址为ebp-0F8h 的内存空间(addr)赋值给ecx
00F714F9 mov dword ptr [ebp-100h],ecx //ecx的值(addr)赋给起始地址为ebp-100h的内存空间
00F714FF jmp main+5Bh (0F7150Bh) //无条件跳转到0x0F7150B
00F71501 mov dword ptr [ebp-100h],0 //起始地址为ebp-100h的内存空间置为0---分配失败的情况
00F7150B mov edx,dword ptr [ebp-100h] //起始地址为ebp-100h的内存空间(addr)赋值给edx
00F71511 mov dword ptr [obj],edx  //把edx的内容(addr)赋值给obj为起始地址的内存空间a
10: delete obj;
00F71514 mov eax,dword ptr [obj]  //把obj为起始地址的内存空间送往eax
00F71517 mov dword ptr [ebp-0ECh],eax //eax送往以ebp-0ECh为起始地址的内存空间
00F7151D mov ecx,dword ptr [ebp-0ECh]  //以ebp-0ECh为起始地址的内存空间送往ecx
00F71523 push ecx //ecx压栈
00F71524 call operator delete (0F710A0h)
00F71529 add esp,4  
11:int *obj2 = new int(7);
与分配obj相同

  

 此时输出*obj,会拿到“7”。 why? obj本来是依靠于分配的地点addr_obj,
删除操作就是去除掉addr_obj中的始末,释放addr_obj内存,然而obj本身仍旧靠为其的
当大家于obj2分红空间时,new函数会回到第一单可用之地址,也即是刚释放的addr_obj,此时obj依旧凭借于该地点,所以取上述结果。
这样做即便偶尔会正确,不过后患无穷!!!     addr的经的地点 eax
-> ptr [ebp-0F8h] -> ecx ->  ptr [ebp-100h] -> edx ->
ptr[obj]   new函数所设开的 1.
调用对象的构造函数,分配空间,再次来到地址addr 2.
将再次来到的地点addr赋值给指针对象

http://www.bkjia.com/cjjc/1097797.htmlwww.bkjia.comtruehttp://www.bkjia.com/cjjc/1097797.htmlTechArticleC++ new到底new什么,到底new 9: int *obj = new
int(6);00F714CE push 4 //压栈00F714D0 call operator new (0F711EAh)
//调用new函数重返分配的地方addr — 0F711EAh是指…

打造一百单兵卒

倘要创制 100 独战士怎么处置为?

循环 100 次吧:

JavaScript

var 士兵们 = [] var 士兵 for(var i=0; i<100; i++){ 士兵 = { ID: i,
// ID 不能更 兵种:”美利坚合众国小将”, 攻击力:5, 生命值:42, 行走:function(){
/*挪俩步的代码*/}, 奔跑:function(){ /*狂奔的代码*/ },
死亡:function(){ /*Go die*/ }, 攻击:function(){ /*贴他熊脸*/ },
防御:function(){ /*护脸*/ } } 士兵们.push(士兵) }
兵营.批量创制(士兵们)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var 士兵们 = []
var 士兵
for(var i=0; i<100; i++){
  士兵 = {
    ID: i, // ID 不能重复
    兵种:"美国大兵",
    攻击力:5,
    生命值:42,
    行走:function(){ /*走俩步的代码*/},
    奔跑:function(){ /*狂奔的代码*/  },
    死亡:function(){ /*Go die*/    },
    攻击:function(){ /*糊他熊脸*/   },
    防御:function(){ /*护脸*/       }
  }
  士兵们.push(士兵)
}
 
兵营.批量制造(士兵们)

哎呀好简单。

质疑

面的代码是一个题材:浪费了无数内存。

  1. 履、奔跑、死亡、攻击、防御这五单动作对每个士兵其实是均等的,只需要各自引用和一个函数就足以了,没必要再成立100 单走、100单奔跑……
  2. 这多少个精兵的兵种和攻击力都是一律的,没必要创制 100 次。
  3. 只有出 ID 和生命值需要成立 100 次,因为每个士兵发出好的 ID 和生命值。

改进

关押了我们的专辑在此以前著作(JS
原型链
)的同窗肯定知道,用原型链可以缓解重复创造的题材:我们先创设一个「士兵原型」,然后于「士兵」的
__proto__ 指向「士兵原型」

JavaScript

var 士兵原型 = { 兵种:”美利坚同盟国兵”, 攻击力:5, 行走:function(){
/*动俩步的代码*/}, 奔跑:function(){ /*狂奔的代码*/ },
死亡:function(){ /*Go die*/ }, 攻击:function(){ /*贴他熊脸*/ },
防御:function(){ /*护脸*/ } } var 士兵们 = [] var 士兵 for(var i=0;
i<100; i++){ 士兵 = { ID: i, // ID 不可知还 生命值:42 }
/*实际工作负永不这样勾画,因为 __proto__ 不是专业属性*/
士兵.__proto__ = 士兵原型 士兵们.push(士兵) } 兵营.批量创立(士兵们)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var 士兵原型 = {
  兵种:"美国大兵",
  攻击力:5,
  行走:function(){ /*走俩步的代码*/},
  奔跑:function(){ /*狂奔的代码*/  },
  死亡:function(){ /*Go die*/    },
  攻击:function(){ /*糊他熊脸*/   },
  防御:function(){ /*护脸*/       }
}
var 士兵们 = []
var 士兵
for(var i=0; i<100; i++){
  士兵 = {
    ID: i, // ID 不能重复
    生命值:42
  }
 
  /*实际工作中不要这样写,因为 __proto__ 不是标准属性*/
  士兵.__proto__ = 士兵原型
 
  士兵们.push(士兵)
}
 
兵营.批量制造(士兵们)

优雅?

有人提议创立一个兵士的代码分散在少单地方很不优雅,于是我们因此一个函数把那点儿片段关联起来:

JavaScript

function 士兵(ID){ var 临时对象 = {} 临时对象.__proto__ = 士兵.原型
临时对象.ID = ID 临时对象.生命值 = 42 return 临时对象 } 士兵.原型 = {
兵种:”米国大兵”, 攻击力:5, 行走:function(){ /*动俩步的代码*/},
奔跑:function(){ /*狂奔的代码*/ }, 死亡:function(){ /*Go die*/ },
攻击:function(){ /*贴他熊脸*/ }, 防御:function(){ /*护脸*/ } } //
保存也文件:士兵.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function 士兵(ID){
  var 临时对象 = {}
 
  临时对象.__proto__ = 士兵.原型
 
  临时对象.ID = ID
  临时对象.生命值 = 42
  
  return 临时对象
}
 
士兵.原型 = {
  兵种:"美国大兵",
  攻击力:5,
  行走:function(){ /*走俩步的代码*/},
  奔跑:function(){ /*狂奔的代码*/  },
  死亡:function(){ /*Go die*/    },
  攻击:function(){ /*糊他熊脸*/   },
  防御:function(){ /*护脸*/       }
}
 
// 保存为文件:士兵.js

接下来就足以心情舒畅地引用「士兵」来成立士兵了:

JavaScript

var 士兵们 = [] for(var i=0; i<100; i++){ 士兵们.push(士兵(i)) }
兵营.批量创立(士兵们)

1
2
3
4
5
6
var 士兵们 = []
for(var i=0; i<100; i++){
  士兵们.push(士兵(i))
}
 
兵营.批量制造(士兵们)

JS 之大之眷顾

JS 之大创设了 new 关键字,可以被我们少写几推行代码:

图片 2

假设您以兵前边使用 new 关键字,那么可以少开四码业务:

  1. 决不创设临时对象,因为 new 会帮您开(乃采用「this」就可拜到临时对象);
  2. 无须绑定原型,因为 new 会帮您做(new
    为了通晓原型在啊,所以指定原型的名也 prototype);
  3. 决不 return 临时对象,因为 new 会帮您做;
  4. 永不为原型想名字了,因为 new 指定名字也 prototype。

这无异于潮大家所以 new 来描写

JavaScript

function 士兵(ID){ this.ID = ID this.生命值 = 42 } 士兵.prototype = {
兵种:”米利坚兵”, 攻击力:5, 行走:function(){ /*举手投足俩步的代码*/},
奔跑:function(){ /*狂奔的代码*/ }, 死亡:function(){ /*Go die*/ },
攻击:function(){ /*贴他熊脸*/ }, 防御:function(){ /*护脸*/ } } //
保存也文件:士兵.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function 士兵(ID){
  this.ID = ID
  this.生命值 = 42
}
 
士兵.prototype = {
  兵种:"美国大兵",
  攻击力:5,
  行走:function(){ /*走俩步的代码*/},
  奔跑:function(){ /*狂奔的代码*/  },
  死亡:function(){ /*Go die*/    },
  攻击:function(){ /*糊他熊脸*/   },
  防御:function(){ /*护脸*/       }
}
 
// 保存为文件:士兵.js

然后是创办士兵(加了一个 new 关键字):

JavaScript

var 士兵们 = [] for(var i=0; i<100; i++){ 士兵们.push(new 士兵(i))
} 兵营.批量创立(士兵们)

1
2
3
4
5
6
var 士兵们 = []
for(var i=0; i<100; i++){
  士兵们.push(new 士兵(i))
}
 
兵营.批量制造(士兵们)

new
的意图,就是探望那么几推行代码。(也便是所谓的语法糖)

注意 constructor 属性

new
操作以记录「临时对象是出于何人函数创造的」,所以先给「士兵.prototype」加了一个
constructor 属性:

JavaScript

士兵.prototype = { constructor: 士兵 }

1
2
3
士兵.prototype = {
  constructor: 士兵
}

若果你再对「士兵.prototype」赋值,那么这些 constructor
属性就无了,所以您应该如此形容:

JavaScript

士兵.prototype.兵种 = “弥利坚战士” 士兵.prototype.攻击力 = 5
士兵.prototype.行走 = function(){ /*动俩步的代码*/}
士兵.prototype.奔跑 = function(){ /*狂奔的代码*/ } 士兵.prototype.死亡
= function(){ /*Go die*/ } 士兵.prototype.攻击 = function(){
/*贴他熊脸*/ } 士兵.prototype.防御 = function(){ /*护脸*/ }

1
2
3
4
5
6
7
士兵.prototype.兵种 = "美国大兵"
士兵.prototype.攻击力 = 5
士兵.prototype.行走 = function(){ /*走俩步的代码*/}
士兵.prototype.奔跑 = function(){ /*狂奔的代码*/  }
士兵.prototype.死亡 = function(){ /*Go die*/    }
士兵.prototype.攻击 = function(){ /*糊他熊脸*/   }
士兵.prototype.防御 = function(){ /*护脸*/       }

或者你吗得以自己为 constructor 重新赋值:

JavaScript

士兵.prototype = { constructor: 士兵, 兵种:”United States小将”, 攻击力:5,
行走:function(){ /*挪俩步的代码*/}, 奔跑:function(){ /*狂奔的代码*/
}, 死亡:function(){ /*Go die*/ }, 攻击:function(){ /*贴他熊脸*/ },
防御:function(){ /*护脸*/ } }

1
2
3
4
5
6
7
8
9
10
士兵.prototype = {
  constructor: 士兵,
  兵种:"美国大兵",
  攻击力:5,
  行走:function(){ /*走俩步的代码*/},
  奔跑:function(){ /*狂奔的代码*/  },
  死亡:function(){ /*Go die*/    },
  攻击:function(){ /*糊他熊脸*/   },
  防御:function(){ /*护脸*/       }
}

完。

2 赞 6 收藏 4
评论

图片 3

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图