【转】JS 的 new 到底是干什么的?

原文:https://zhuanlan.zhihu.com/p/23987456?refer=study-fe

大部分讲 new 的文章会从面向对象的思路讲起,但是我始终认为,在解释一个事物的时候,不应该引入另一个更复杂的事物。

今天我从「省代码」的角度来讲 new。

---------------------------

想象我们在制作一个策略类战争游戏,玩家可以操作一堆士兵攻击敌方。

我们着重来研究一下这个游戏里面的「制造士兵」环节。

一个士兵的在计算机里就是一堆属性,如下图:

我们只需要这样就可以制造一个士兵:

var 士兵 = {
  ID: 1, // 用于区分每个士兵
  兵种:"美国大兵",
  攻击力:5,
  生命值:42,
  行走:function(){ /*走俩步的代码*/},
  奔跑:function(){ /*狂奔的代码*/  },
  死亡:function(){ /*Go die*/    },
  攻击:function(){ /*糊他熊脸*/   },
  防御:function(){ /*护脸*/       }
}

兵营.制造(士兵)

制造一百个士兵

如果需要制造 100 个士兵怎么办呢?

循环 100 次吧:

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__ 指向「士兵原型」

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(士兵)
}

兵营.批量制造(士兵们)

优雅?

有人指出创建一个士兵的代码分散在两个地方很不优雅,于是我们用一个函数把这两部分联系起来:

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))
}

兵营.批量制造(士兵们)

JS 之父的关怀

JS 之父创建了 new 关键字,可以让我们少写几行代码:

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

  1. 不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);
  2. 不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype);
  3. 不用 return 临时对象,因为 new 会帮你做;
  4. 不要给原型想名字了,因为 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 的作用,就是省那么几行代码。(也就是所谓的语法糖)

注意 constructor 属性

new 操作为了记录「临时对象是由哪个函数创建的」,所以预先给「士兵.prototype」加了一个 constructor 属性:

士兵.prototype = {
  constructor: 士兵
}

如果你重新对「士兵.prototype」赋值,那么这个 constructor 属性就没了,所以你应该这么写:

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

或者你也可以自己给 constructor 重新赋值:

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

完。

原文地址:https://www.cnblogs.com/PeunZhang/p/12298391.html

时间: 2024-10-12 23:18:18

【转】JS 的 new 到底是干什么的?的相关文章

电脑中的TEC到底是干什么来用的

电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的电脑中的TEC到底是干什么来用的 http://www.merryread.com/TouGao/ShowArticle.aspx?HangBiaoShi=72848http://www.merryread.com/TouGao/ShowArt

企业会计到底是干什么的

企业会计到底是干什么的_解读天津市代理记账公司 天津市代理记账公司会计是干什么的?虽然,天津市代理记账公司至今仍能将会计的定义背得烂熟,但通过会计实践,我已然对会计的内涵有了更深更形象的认识.天津市代理记账公司会计其实是医生.守门员.项链. 会计是医生 会计与医生是两个职业,有着异曲同工之妙.医生让病人药到病除,而会计则为企业诊脉问切.天津市代理记账公司会计人的工作就是利用会计核算方面的专门方法,通过核算.分析和研究,找出企业在生产经营中存在的问题,并提出相关的改进意见,使企业不断提高其经济效益

从TP-Link到雷蛇,纷纷入局智能手机业到底想干什么?

"眼看他起朱楼,眼看他宴宾客,眼看他楼塌了",这句形容世态炎凉的话其实与智能手机市场更为相像.诺基亚的辉煌与没落.黑莓的强势与消声无迹.摩托罗拉的数次易手.小米的横空出世与崛起.苹果的踱步不前.山寨厂商的顽强生存--呈现各种态势的手机厂商,就像是一部包含万千生态的社会人情画卷. 而现在,又有企业按捺不住杀进了手机行业--TP-Link及雷蛇.它们并不是传统意义上的手机厂商,但又不是毫无根据的"门外汉"--已经在硬件领域积累了不少经验.一家是路由器大厂,一家是知名的游

Python到底可以干什么?主要应用领域

如果说挑选一门编程语言进行学习,你会选择哪个?当然是Python.Python是一门简单的编程语言,适合初学者学习,也是很多人都喜欢的语言,那么Python到底可以干什么? Python语言在学术上是非常受欢迎的,不是计算机专业的人,都可以学习Python.这个语言的前景是不可限量的,语法是非常容易理解的,很多人可以减去学习的负担,没有压力. Python到底可以干什么?主要应用领域: 1.Linux运维:Linux运维是必须并且一定要掌握的Python语言,Python是非常厉害的语言,可以满

JavaScript-读 You Dont Know JS,this到底是什么

前端技术更新很快,几个月前我还在写React,现在又有人建议我学学Vue了.思考之后决定先沉下心来补补JavaScript基础.You Dont Know JS一系列书不错.这一系列博客是我读这本书以后总结的干货. 为什么要使用this 知乎的习惯是先问是什么,再问为什么,到这篇博客却要翻过来讲.如果没有需要使用this的场景,那我们就没必要知道this到底是什么了. 请看下面的例子: // 使用this的动机 function tellName(){ console.log('name is'

js 中this到底指向哪里?

其实js的this指向很简单.我们记住下面3种情况. this 指向的是浏览器中的window.代码如下: function fn(){ this.name='yangkun'; this.age=28; } 当我们执行fn()的时候,这个普通函数中的this指向到底是什么?答案就是指向的是浏览器中的window.(这里说明,这里实在浏览器下,如果是node环境不是). 如果这个时候我们对上面的函数进行进一步操作: function fn(){ this.name='yangkun'; this

引入CSS和JS文件时 到底应该在head标签中还是body中?

引入CSS在head中, JS建议在body的尾部引入; 这样有利于加载速度 <!DOCTYPE html> <html>   <head>      <!--网页页面字符集-->     <meta charset="utf-8">       <!--让IE使用最新的渲染模式-->     <meta http-equiv="X-UA-Compatible" content="

JS的定时到底有多不准

博客逐步迁移到,独立博客,原文地址,http://www.woniubi.cn/js_hide_tab_setinterval/ 我们一直都在说,JS的定时非常的不准确,但是很少有人去验证他,今天我就来验证一下.首先我们准备一个简单的例子. 简单例子 每隔一秒钟,然后输出离最开始的时间差距有多大. <script type="text/javascript"> var begin = (new Date()).getTime(); var i = 0; setInterva

[JS] this, 你到底指向谁?

JS中, this的值取决于调用的模式, 而JS中共有4中调用模式: 1. 方法调用模式 当一个函数被保存为对象的一个属性时, 我们称它为一个方法, 当一个方法被调用时, this指向该对象, 如: var obj = {  value: 1,  getValue: function() {   alert(this.value);  } }; obj.getValue(); // 输出1, 此时的this指向obj 注意: 该模式中, this到对象的绑定发生在方法被调用的时候. 2. 函数调