JS 的 new 到底是干什么的?

news2024/10/7 3:23:46

大部分讲 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(){ /*护脸*/       }
}

完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络编程【网络编程基本概念、 网络通信协议、IP地址 、 TCP协议和UDP协议】(一)-全面详解(学习总结---从入门到深化)

目录 网络编程基本概念 网络通信协议 IP地址 TCP协议和UDP协议 网络编程基本概念 计算机网络 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其 外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软 件及网络通信协议的…

echarts图表进度条类型图

1、实现效果 左边是类别、数量&#xff0c;中间类似于进度条&#xff0c;右边是所占百分比 2、实现思路 x轴不显示&#xff0c;y轴的axisLabel用富文本&#xff0c;显示机器与台数&#xff1b;图表有两个数据组&#xff0c;分别用蓝色和灰色表示&#xff0c;两个柱子重合&…

Jmeter beanshell编程实例

目录 1、引言 2、需求 3、BeanShell实现 3.1、原始单元测试的java代码&#xff1a; 3.2、调用的RSAUtil原始方法&#xff1a; 3.3、使用BeanShell预处理器实现报文加密&#xff1a; 库导入部分&#xff1a; JSON报文组装&#xff1a; RSA加密&#xff1a; 3.4、取样器…

【C语言】指针进化:传参与函数(2)

莫道君行早&#xff0c;更有早行人。— 出自《增广贤文上集》 解释&#xff1a;别说你出发的早&#xff0c;还有比你更早的人。 这篇博客我们将会深入的理解数组传参和函数指针等指针&#xff0c;是非常重要的内容&#xff0c;学好这部分才能算真正学懂C语言。 目录 一维数组传…

【网络】socket——预备知识 | 套接字 | UDP网络通信

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 在前面本喵对网络的整体轮廓做了一个大概的介绍&#xff0c;比如分层&#xff0c;协议等等内容&#x…

UFS 14 - UFS RPMB安全读写命令

UFS 14 - UFS RPMB安全读写命令 1 SECURITY PROTOCOL IN/OUT Commands1.1 CDB format of SECURITY PROTOCOL IN/OUT commands1.2 Supported security protocols list description1.3 Certificate data description 2 CDB format of SECURITY PROTOCOL IN/OUT commands demo2.1 …

Spring Boot原理分析(一):项目启动流程、自动装配

文章目录 一、项目启动流程二、SpringBootApplication.java源码解析1.准备工作2.源码3.自定义注解4.组合注解5.注解ComponentScan过滤器 6.注解SpringBootConfigurationConfiguration 7.注解EnableAutoConfiguration&#xff08;1&#xff09;Spring手动装配使用XML配置文件使用…

Nerf论文阅读笔记Neuralangelo: High-Fidelity Neural Surface Reconstruction

Neuralangelo&#xff1a;高保真神经表面重建 公众号&#xff1a;AI知识物语&#xff1b;B站暂定&#xff1b;知乎同名 视频入门介绍可以参考 B站——CVPR 2023最新工作&#xff01;Neuralangelo&#xff1a;高保真Nerf表面重建 https://www.bilibili.com/video/BV1Ju411W7…

杨氏矩阵,字符串左旋,字符串旋转结果题目解析

杨氏矩阵 题目要求:有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 示例 分析:我们仔细分析&#xff0c;不难发现&#xff0c;对于杨氏矩阵老说&#xff0c;右上角和左下…

leetcode1020. 飞地的数量

https://leetcode.cn/classic/problems/number-of-enclaves/description/ 给你一个大小为 m x n 的二进制矩阵 grid &#xff0c;其中 0 表示一个海洋单元格、1 表示一个陆地单元格。 一次 移动 是指从一个陆地单元格走到另一个相邻&#xff08;上、下、左、右&#xff09;的…

哈希的应用->位图

ps&#xff1a;左移位并不是向左移动位&#xff0c;而是低数据位向高数据位挪动 位图&#xff08;主要接口&#xff0c;set(size_t)标识、reset(size_t)取消、test(size_t) 查看 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一…

做软件测试到底要不要学编程?

乔布斯曾经说过“每个人都应该学习编程&#xff0c;因为它会教你如何思考”&#xff0c;看&#xff0c;乔帮主都觉得所有人都应该学编程&#xff0c;那你说做测试的要不要学&#xff1f;当然要。 作为测试人员&#xff0c;除了上面这个原因&#xff0c;我觉得如果会编程&#x…

Android架构之MVC,MVP,MVVM解析

MVC架构 View&#xff1a;Acitivity(View)、Fragment(View)视图&#xff0c;在android里xml布局转成View后&#xff0c;加载到了Activity/Fragment里了。 Controller&#xff1a;Controller对应着Activity/Fragment&#xff0c;绑定UI&#xff0c;处理各种业务。 Model&#xf…

python接口自动化(三十)--html测试报告通过邮件发出去——中(详解)

简介 上一篇&#xff0c;我们虽然已经将生成的最新的测试报告发出去了&#xff0c;但是MIMEText 只能发送正文&#xff0c;无法带附件&#xff0c;因此我还需要继续改造我们的代码&#xff0c;实现可以发送带有附件的邮件。发送带附件的需要导入另外一个模块 MIMEMultipart。还…

java版电子招标采购系统源码之电子招标采购实践与展望-招标采购管理系统

统一供应商门户 便捷动态、呈现丰富 供应商门户具备内外协同的能力&#xff0c;为外部供应商集中推送展示与其相关的所有采购业务信息&#xff08;历史合作、考察整改&#xff0c;绩效评价等&#xff09;&#xff0c;支持供应商信息的自助维护&#xff0c;实时风险自动提示。…

springboot+MySQL实现4S店车辆管理系统

本系统为了数据库结构的灵活性所以打算采用MySQL来设计数据库&#xff0c;而java技术&#xff0c;B/S架构则保证了较高的平台适应性。本文主要介绍了本系统的开发背景&#xff0c;所要完成的功能和开发的过程&#xff0c;主要说明了系统设计的重点、设计思想。

计算机体系结构基础知识介绍之使用多问题和静态调度来利用 流水线

为了提高处理器的性能&#xff0c;我们需要让每个时钟周期内发出多条指令&#xff0c;而不是只发出一条。这种多发射处理器有三种主要类型&#xff1a; 1. 静态调度的超标量处理器 2. VLIW&#xff08;非常长指令字&#xff09;处理器 3. 动态调度的超标量处理器。 这三种类型的…

lua 请求ftp服务器数据,下载文件

1、装入ftp库 2、调用ftp的get()方法 3、get()方法参数格式&#xff1a; 4、将返回到的数据写入文件中 例如&#xff0c;本次获取专利数据系统 http://patdata1.cnipa.gov.cn/ 的ftp站点数据 local ftp require("socket.ftp")--此处我没填端口号 file,err ftp.g…

findfont: Font family ‘Times New Roman‘ not found.

问题 Linux 使用 matplotlib.pyplot 画图时为了使字体和英文论文中的 Times of Roman 一致&#xff0c;通常会用到如下文本格式 font1 {family: Times New Roman, # x and y labelsweight: normal,size: 16}但在实际使用时会出现如下报警信息&#xff1a; findfont: Font …

element ui 导入模块的封装

导入组件的封装 <template><Modal :visible"visible" title"导入" onSave"onSave" onCancal"closeDialog"><template #default><el-upload ref"upload" class"upload-demo"action"ht…