前端工程化
ES6+
ECMAScript是标准,JavaScript是其主要实现。
ES5的严格模式
1 |
◆严格模式下八进制的限制
严格模式下不允许使用0开头的八进制数字表示法(如012)。会抛出Uncaught SyntaxError: Octal literals are not allowed in strict mode。不过允许使用十六进制表示法(如0x12)。
设计原因: 数字前导零存在语义歧义(数学上012应与12等价),属于历史遗留问题。
◆严格模式下变量声明的限制
不允许未声明直接使用变量(如username = 100),会抛出username is not defined错误。
非严格模式对比: 普通模式下会自动创建全局变量,属于隐式变量声明。
let 与 const
◆ES6新增了let命令。
- 不存在变量提升:let不像var那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。
- 暂时性死区:只要块级作用域内存在let命令,它所声明的变量就“绑定”这个区域,不再受外部的影响。
- 不允许重复声明:let不允许在相同作用域内重复声明同一个变量。
ES6规定暂时性死区和不存在变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。 这样的错误在ES5中是很常见的,现在有了这种规定,避免此类错误就很容易了。
◆ES5只有全局作用域和函数作用域,ES6增加了块级作用域。
这其实带来了很多不合理的场景,内层变量可能会覆盖外层变量,用来计数的循环变量泄露为全局变量。
立即执行函数设计目的:隔离变量,避免污染全局作用域。 ES5 中 var 声明的变量无块级作用域,即使写在 {} 里,也会泄漏到外层(甚至全局)。IIFE 的解决方式就是用函数作用域包裹变量,强制隔离,避免泄漏。
所以,块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。
◆const与let不同的是,常量不可变,声明时必须立即赋值。
解构赋值
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这就是解构(Destructuring)。
数组解构赋值
只要具有索引结构(通过下标访问元素),就可以解构。所以伪数组也可以。(字符串,arguments,DOM元素集合等)
1 | /** 错误 易混点 |
◆关于默认值
解构赋值允许指定默认值。.ES6内部使用严格相等运算符(===)判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。
1 | var [f = true] = []; // f = true |
对象的解构赋值
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
1 | var {foo, bar} = {foo: "aaa", bar: "bbb"}; |
所以,对象的解构赋值是以下形式的简写:
1 | var {foo: foo, bar: bar} = {foo: "aaa", bar: "world"}; |
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
指定默认值
1 | var {n1,n2:n2=22,n3=33,n4} = {n1:11}; |
◆解构数组为对象
1 | var {length, push, map} = [1, 2, 3, 4, 5]; |
数组本质也是对象,具有 length 等内置属性。它可以解构出数组的内置方法和属性。
◆解构字符串为对象
1 | var {length, indexOf, forEach} = "hello world"; |
字符串新增特性
◆模板字符串
特点:
- 内部可以直接换行
- 内部可以直接插入变量或表达式 例如:
${name},${age + 10}
典型应用:用模板字符串动态生成HTML内容很方便
◆ES5 方法
charAt()
charCodeAt() 字符 -> 索引
indexOf() 返回子串首次出现的位置
lastIndexOf() 返回子串最后出现的位置
slice()
substring()
substr()
toLowerCase()
toUpperCalse()
split() 将字符串分割为数组
search() 正则搜索
match() 正则匹配
replace() 正则替换
◆ES6 + 方法
repeat(n) 字符串重复,返回新字符串
includes(‘a’) 判断是否包含a,返回布尔值
startsWith() 判断是否以某个值开始,返回布尔值
endsWith() 判断是否以某个值结尾,返回布尔值
trim() 去掉两端的空格
trimStart() 去掉前面的空格(ES2019)
trimEnd() 去掉后面的空格(ES2019)
padStart() 前面填充字符串(ES2017)
padEnd() 后面填充字符串(ES2017)
replaceAll() 替换字符串中指定内容,替换所有(ES2021)
1 | 'Hello World'.padStart(20) // 在前面补9个空格使总长度达20 |
数值新增特性
二进制:使用0b前缀(如0b10表示二进制的2)
八进制:使用0o前缀(如0o10表示八进制的8)
十六进制仍然使用0x前缀。
输出特性:无论使用何种进制表示,控制台输出都会转换为十进制形式
Number 构造函数本身新增的方法和属性:
ES5
1 | Number.MAX_VALUE; |
ES6+
Number.MAX_SAFE_INTEGER 读取最大的安全整数
Number.MIN_SAFE_INTEGER 读取最小的安全整数
Number.EPSILION 两个数字间最小差值,就是JS的数字精度
Number.isNaN() 同全局对象的 isNaN()
Number.isFinite() 同全局对象的 isFinite()
Number.parseInt() 同全局对象的 parseInt()
Number.parsetFloat() 同全局对象的 parsetFloat()
Number.isInteger() 判断参数是否是整数,返回布尔值
Number.isSafeInteger() 判断参数是否是安全整数,返回布尔值
Git
- github https://github.com/
- 码云 https://gitee.com/
开始
Git 是一款开源免费的分布式的版本控制系统。是 Linux 之父 Linus Torvalds(林纳斯·托瓦兹)为了方便管理 linux 代码代码而开发的。
功能:
- 代码备份
- 版本回退
- 多人协作
- 权限控制
Git 官方文档地址: https://git-scm.com/book/zh/v2
◆常用Linux命令ls 将当前目录下所有的文件或子目录列举 ls -l 详细信息
rm -rf 删除(小心rm -rf /,全部格式化删除clear 删除当前的执行记录
cd 进入到指定的目录
◆常用快捷键
ctrl + c 终止当前命令
ctrl + l 清除当前命令行
tab 自动补全路径
键盘上下方向键 调取历史命令
◆Vim的使用
1 | vim <文件名.txt> |
Git基础命令
初始化:每个新项目需要执行一次git init初始化,初始化会在项目目录下创建.git隐藏目录。
.git目录 :
- hooks 目录包含客户端或服务端的钩子脚本,在特定操作下自动执行。
- info 包含一个全局性排除文件,可以配置文件忽略。
- logs 保存日志信息。
- objects 目录存储所有数据内容,本地的版本库存放位置。
- refs 目录存储指向数据的提交对象的指针(分支)。
- config 文件包含项目特有的配置选项。
- description 用来显示对仓库的描述信息。
- HEAD 文件指示目前被检出的分支。
- index 暂存区数据。
不要手动去修改 .git 文件夹中的内容。
- 工作区:代码编辑区,开发者直接编辑文件的地方。所有修改首先在工作区进行。
- 暂存区:修改待提交区。通过
git add命令将工作区修改添加到暂存区。 - 版本库:存储项目历史版本。通过
git commit命令将暂存区内容提交到版本库。除了.git文件,其他都是工作区。
添加暂存区:
1 | git add <file> # 添加指定文件到暂存区 |
提交版本库:
1 | git commit -m "提交日志" # 把暂存区的东西提交到版本库 |
查看状态和变化:
1 | git status; |
如果 git status 命令的输出对于你来说过于简略,而你想知道具体修改了什么地方,可以用 git diff 命令。
1 | git diff # 查看当前工作区和版本库的差异 (不包括新增的文件) |
撤销修改和撤销暂存
◆工作区的修改没有添加到暂存区
1 | git restore <文件名> # 恢复工作区指定文件 |
◆工作区的修改已经添加到暂存区
如果工作区的修改已经添加到暂存区,先清除暂存区,再恢复工作区。
1 | git restore --staged <文件名> # 把指定文件从暂存区移除 |
历史版本回滚
查看历史版本号:
1 | git log # 查看提交记录 |
通过指定版本号回滚:
1 | git reset --hard <commitID> # 版本号前七位即可 |
如果需要查看被回滚掉的提交的版本号:
1 | git reflog |
快捷回滚:
1 | git reset --hard HEAD^ # 恢复到上个版本 |
Git忽略文件
哪些文件需要被 git 忽略:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如 Java 编译产生的
.class文件; - 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
◆设置忽略文件 .gitignore
忽略文件的文件名是 .gitignore 的文件, 文件内可以设置项目的忽略规则。
忽略文件可以放在项目中的任意目录中,放在哪个目录作用范围就是哪个目录; 一般忽略文件会放在项目的根目录下。
Node与模块化
官方网站地址: https://nodejs.org/en/
中文网站地址 : http://nodejs.cn/
开始
Node.js,也称 Node,是一个基于 Chrome V8 引擎的 JavaScript 运行环境(宿主),与浏览器是等价的。
Node.js不是一种独立的语言,也不是一个JS框架或库,运行在Node.js上的JS不能使用DOM,BOM,但是可以使用Node.js提供的各种API。
有什么用?
- 基于Node进行后端开发
- 前端工程化支持(Wepack/Gulp等工具链)
- 开发桌面应用(框架Electron、ReactNative等)
- 开发小工具(自动化脚本、爬虫程序等)
特点:
- 单线程
- 非阻塞
- 事件驱动
前端是指运行在客户端上的代码,特指WEB前端时即运行在浏览器上的代码,如HTML、CSS和JavaScript构建的页面。
后端是指运行在服务器端的程序,主要负责业务逻辑实现、数据库操作等功能,也称为服务端。
◆后端开发的组成
- 后端编程语言(PHP、Java、C#、Go、Python、JavaScript)
- Web服务器程序(Tomcat、Apache、Nginx、IIS等)
- 数据库程序(MySQL、Oricle、MongoDB)
后端架构分层
- 表现层:Node.js
- 业务层:Java等
- 底层:C/C++
cmd :不支持ls等Linux命令
PowerShell : 比cmd强大
GitBash : 支持完整Linux命令
◆REPL方式运行
(临时计算或快速测试)
进入REPL:命令行或终端运行 node ,就进入了 repl 模式
退出REPL:.exit 或者 按两下 ctrl+c 或者 ctrl+d
常用REPL命令:
ctrl + c- 按下两次 - 退出 Node REPL。ctrl + d- 退出 Node REPL.- 向上/向下键 - 查看输入的历史命令
tab键 - 列出当前变量(对象)
◆脚本运行
◆内置常量dirname 获取JS脚本所在目录的绝对路径__filename 获取JS脚本自己的绝对路径
1 | console.log(dirname); |
Buffer
Buffer 是 Node.js 提供的用于存储二进制数据的类数组对象(长得像数组,但本质是底层内存的直接映射),它的大小在创建时固定,无法动态调整,且存储的是 0~255 之间的整数值(对应字节)。
1 byte 字节 = 8 Bit (1Bit对应的是一个二进制位)
1024 byte = 1KB
一个 UTF-8 的中文字符大多数情况都是占 3 个字节。
创建Buffer
1 | const b1 = Buffer.alloc(12); // 创建12字节Buffer并自动填充0 |
读取Buffer
1 | const b = Buffer.from("hello 嘻嘻嘻"); |
buffer 每个元素能表示的最大数字是 255,如果超过 255 的数字,会舍去高位(二进制)
1 | buff3[0] = 365; // 0001 0110 1101 |
为什么给 Buffer 的元素赋值 365,最终输出却是 109?
Buffer 的每个元素本质是 8 位无符号整数(0~255),只能存储 8 个二进制位,超过 255 的数字会被截断高位,只保留最后 8 位二进制数,再转为十进制。
内置模块
Noode 当中的模块分为三种:内置模块,第三方模块以及自定义模块。 不论哪一种模块,在使用时都必须先引入模块。
1 | const 变量 = require('模块'); |
关于抛错:
- try 里面的错误会被 catch 捕获,不论是代码错误还是主动抛出,捕获到错误之后由程序员处理,系统不会报错
- try catch 不论是否抛出错误,都不影响后面的语句的执行
- try 内部,错误后面的语句不会执行
1 | try { |
Path模块
path.join([path1][, path2][, ...])用于连接路径。该方法的主要用途在于,会正确使用当前系统的路径分隔符,Unix系统是”/“,Windows系统是”\“。path.isAbsolute(path)判断参数path是否是绝对路径。path.dirname(p)返回路径中目录的部分 。path.basename(p)返回路径中的最后一部分,文件名部分。path.extname(p)返回路径中文件的后缀名。path.resolve()将路径或者路径片段序列化为绝对路径 (常用)。
fs模块(文件系统模块)
文件读取:
1 | // 引入模块 |
写入文件:
1 | // 导入模块 |
文件重命名:
1 | // 导入模块 |
删除文件:
1 | const fs = require('fs'); |
创建目录:
1 | // 异步方式 创建目录 递归方式创建多级目录 |
URL模块
querystring 模块
JSON 格式的处理
JSON全称是 JavaScript Object Notation (JavaScript 对象表示法) ,是一种轻量级的数据交换格式。
JSON 的语法与 JS 定义数组和对象的语法区别:
- json 中的字符串必须使用双引号
- json 中的属性名必须使用双引号包裹
- json 中的最后一个属性不能有逗号
- json 中的属性值不能是表达式
1 | //将json格式的字符串转为对象 |
Node 中的模块化
四种模块化规范:CommonJS规范、AMD规范、CMD规范、ES Module规范。
Node.js中根据模块来源的不同,将模块分为了3大类,分别是:
- 内置模块(由Node.js官方提供,例如:fs,path,http)。
- 自定义模块:用户创建的每个JS文件,都是自定义模块。
- 第三方模块:由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载。
CommonJS 模块规范
引入模块(自定义模块)
1)使用require()引入模块,内置模块直接写模块名,自定义模块需要些相对路径。
2)自定义模块的地址必须以 ./、 ../ 开头。因为模块文件的地址没有以 ./、 ../ 开头时,会被认为是内置模块或第三方模块的模块名。
3)扩展名可以省略。如.js或.json。无扩展名时会依次查找同名js文件、json文件和目录。
1.扩展名是
.js的模块文件: 读取文件内容并编译执行并获取模块中暴露的数据。
2.扩展名是.json的模块文件: 读取文件时会自动用JSON.parse()解析返回结果作为获取的数据。
整个目录作为一个模块:
1)会默认加载该目录下 package.json 文件中 main 属性定义的入口文件。
2)如果没有package.json, 或者 main 属性对应的文件不存在,则自动找 index.js 、 index.json 作为入口文件。
模块暴露数据
模块中定义了变量但未暴露,外部无法访问。require返回空对象{},无法访问模块内部变量。
模块中通过module.exports赋值暴露数据,绝大多数暴露的是对象类型,因为信息承载量更大。
本质原理:module.exports实际上是模块对外暴露的接口对象,require()的返回值就是这个对象。
1 | module.exports = obj; |
当模块暴露的是对象时,可以使用解构赋值直接获取属性。要注意解构出的方法如果直接调用,this会指向全局对象而非原模块对象。
通过给module.exports添加getMessage和setMessage两个方法,可以实现暴露多个数据。
1 | module.exports.getMessage = () => { |
对象引用与exports赋值的原理 :对象存储在堆中,变量名(如exports)存储在栈中,指向堆中的对象。module.exports默认指向一个空对象,exports是它的别名,也指向这个空对象。给exports添加属性实际上是给这个空对象添加属性。如果修改exports的值(重新赋值),则exports会指向新的对象,与module.exports的引用关系断开,导致无法暴露数据。
ES6 模块规范
引入模块
1 | // 类似解构导入 |
Node.js 要求 ES6 模块采用.mjs后缀文件名。也就是说,只要脚本文件里面使用import关键字,那么就必须采用.mjs后缀名。
如果不希望将后缀名改成.mjs,可以在项目的package.json文件中,指定type字段为module。 此时,只能使用ES6模块语法(import/export)。
模块暴露数据
使用 export default 可以在模块中暴露单个数据,注意文件中 export default 语句只能出现一次。
使用 export 可以暴露多个数据。
1 | // 第一种写法 在声明变量的同时暴露 |
引入模块并使用模块中暴露的数据:
1 | // import 变量名 from '模块地址'; |
NPM
NPM官网:npm | Home
npm类似于手机应用商店或电脑软件管家,是一个集中管理各种开发包的工具平台。官网上托管了大量开发者共享的包,包括流行框架如Vue、React等。此时需要通过命令行工具进行下载,而非直接从网站下载。
1 | npm -v |
package.json
1 | { |
当删除node_modules后运行程序会报错,因为缺少依赖模块。package.json中记录了项目所需的所有依赖包及其版本。通过npm install命令可以自动安装所有依赖。
◆版本锁定规则
^3.0.0:锁定大版本(3.x.x)
~3.1.0:锁定小版本(3.1.x)
3.1.1:锁定完整版本package-lock.json:精确锁定安装时的具体版本。
◆依赖包列表
依赖类型:
- dependencies:生产依赖(开发和运行都需要,项目本身需要)
- devDependencies:开发依赖(仅开发过程需要,项目本身不需要)
安装方式: npm install 包名 --save:安装为生产依赖npm install 包名 --save-dev:安装为开发依赖
模块的查找过程
- 先确定模块名路径是不是以 ./ 或者 ../ 开头,如果不是就认为是内置模块或者第三方模块。
- 再确定有没有该内置模块,如果有该内置模块直接加载;如果没有该内置模块,判定为第三方模块。
- 第三方模块加载过程:
① 先从脚本本就所在的目录中查找有没有 node_modules 目录,如果有进入查找模块。
② 如果脚本同级目录没有 node_modules 目录,去上级目录查找 node_modules 目录,如果有进入查找模块。
③ 以此类推,一直查找到 根目录。
远程仓库与npm
克隆的项目不包含node_modules目录,必须执行npm install才能正常运行项目,依赖安装应在项目根目录下执行。
配置命令别名
配置 package.json 中的 scripts 属性:
1 | { |
配置完成之后,可以使用别名执行命令:
1 | npm run server |
不过 start 别名比较特别,使用时可以省略 run:
1 | npm start |
所以,对于陌生的项目,可以通过查看 scripts 属性来参考项目的一些操作。
cnpm
使用npm下载包时,会去npm官网下载,而npm服务器在国外,可能不稳定。
cnpm使用国内镜像作为npm源,解决官方源在国外导致的下载不稳定问题。淘宝镜像(npmmirror.com)每10分钟同步一次npm官方仓库,提供国内高速下载。
总结特点:
- 与npm命令完全兼容,仅需替换命令前缀。
- 支持所有npm操作(安装、初始化、发布等)。
- 镜像服务器位于阿里云,网络稳定性更高。
全局安装 cnpm 命令,安装完成后使用 cnpm 命令代替 npm 命令。
1 | npm install -g cnpm --registry=https://registry.npmmirror.com |
yarn
Yarn 由 Meta 推出,核心优势是缓存、并行下载、精确版本锁定。
全局安装yarn:
1 | npm install -g yarn |
基础命令:
1 | yarn --version #查看版本 |
发布npm包
HTTP协议
◆IP地址
IP地址是IP协议提供的统一地址格式,为互联网上每个网络和主机分配的唯一标识。每个联网设备(电脑、手机、智能家居等)都必须有唯一IP地址。采用分层结构解决地址不足问题(局域网使用私有IP,对外共用公网IP)。当前主流IPv4格式为点分十进制,地址组合数为256^4种。IPv6是为解决地址不足问题推出的新格式。
◆域名
Express
- 官网: http://expressjs.com/
- 中文网: https://www.expressjs.com.cn/
- Github: https://github.com/expressjs/express



