JS 笔记分享(JS 基础篇 二)

0 阅读38分钟

以下是本人在 js 学习过程中个人总结和积累的一些笔记,我学习或参考过 黑马js、渡一、JavaScript高级程序设计、MDN、al 交流等课程或资料,希望可以帮助到读者,欢迎读者纠偏

本篇是 JS 基础篇 —— 二,包括 DOM、BOM、正则表达式、和函数进阶篇

Window 对象

  1. 注意
    1. window 对象是全局对象,是 JavaScript 中的顶级对象
    2. 如 document , alert( ) , console.log( ) 等都是window的属性,基本 BOM 对的属性和方法都是 window 的
    3. 所有通过 var 定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法
    4. window 对象下的属性和方法调用时可以省略 window

BOM

  1. 含义 BOM(Browser Object Model)是浏览器对象模型

![[z_attachments/Pasted image 20250626223836.png|500]]

定时器——延时函数

  1. 含义 JavaScript 内置的一个用于让代码延迟执行的函数,叫 setTimeout

  2. 语法

setTimeout(回调函数, 等待的毫秒数)
  1. 清除延迟函数
let 变量名 = setTimeout(回调函数, 等待的毫秒数)
clearTimeout(变量名)
  1. 注意
    1. setTimeout 函数仅仅执行一次,可以理解为把一段代码延迟执行,平时省略 window
    2. 延时器需要等待,会先执行后面的代码
    3. 每调用一次延时器都会产生一个新的延时器
    4. 不能在定时器的回调函数内使用 clearTimeout()clearInterval() 因为此时定时器的回调函数正在运行中,不能通过该方式清除定时器,可以使用 定时器id名 = null 清除
    5. 在定时器执行完毕后,定时器会被自动清除,无残留,如果变量引用了id,则id是否存在跟变量是否存在相同

location 对象

  1. 含义 location 地数据类型时对象,它拆分并保存了 URL地址地各个组成部分

href 属性

  1. 作用 获取完整地 URL 地址,对其赋值时用于地址地跳转

  2. 语法

//跳转到百度
location.href = 'http://www.baidu.com'

search 属性

  1. 作用 获取地址中携带的参数,即符号 ? 之后的部分

  2. 语法

console.log(location.search)

hash 属性

  1. 作用 获取地址中的哈希值,即符号 # 后面的部分

  2. 语法

console.log(location.hash)

reload 方法

  1. 作用 刷新当前页面,传入参数 true 时表示强制刷新

  2. 语法

// 刷新页面,从本地取数据
location.reload()
// 强制刷新,从线上取数据 类似于 ctrl +f5
location.reload(true)

navigation 对象

  1. 解释 navigation 的数据类型是对象,该对象记录了浏览器自身的相关信息

userAgent 属性

  1. 作用 检测浏览器的版本及平台

  2. 案例

// 前面加了个!,是立即执行函数,语法为
!function () {}()
  // 检测 userAgent(浏览器信息)
  !(function () {
     const userAgent = navigator.userAgent
     // 验证是否为Android或iPhone
     const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
     const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
     // 如果是Android或iPhone,则跳转至移动站点
     if (android || iphone) {
  	  location.href = 'http://m.itcast.cn'
    }
})();

history 对象

  1. 解释 history 的数据类型是对象,主要管理历史记录,该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等

  2. 常用属性和方法

history对象方法作用
back()后退功能
forward()前进功能
go(参数)前进后退功能,如果参数是1则前进1个页面,参数是-1则后退一个页面,以此类推

本地存储

  1. 含义 将数据存储在用户浏览器

  2. 特点

    1. 设置、读取方便,刷新页面不丢失数据
    2. 容量较大,sessionStoragelocalStorage 大小约 5M 左右
  3. 注意

    1. 本地存储只能存储字符串类型(其他类型会隐式转换为字符串类型)

localStorage

  1. 作用 可以将数据永久存储在本地(用户的电脑),除非手动删除,否则关闭页面也会存在

  2. 特性

    1. 可以多窗口(页面共享)(同一浏览器可以共享)
    2. 键值对的形式存储使用
  3. 语法

    1. 存储数据( key 一般为字符串) localStorage.setItem('key', value)
    2. 读取数据 localStorage.getItem('key')
    3. 删除数据 localStorage.removeItem('key') 删除所有数据: localStorage.clear()
    4. 更改数据 (前提是该key已有值,否则为新建一个键值对进行存储) localStorage.setItem('key', value)

[!tips] 注意

  1. 读取未定义的 item 时,会返回 null

sessionStorage

  1. 特性

    1. 生命周期为关闭浏览器窗口
    2. 在同一个窗口(页面)下数据可以共享
    3. 以键值对的形式存储
    4. 用法跟 localStorage 基本相同
  2. 语法

    1. 存储数据( key 一般为字符串) sessionStorage.setItem('key', value)
    2. 读取数据 sessionStorage.getItem('key')
    3. 删除数据 sessionStorage.removeItem('key') 删除所有数据: sessionStorage.clear()
    4. 更改数据 (前提是该key已有值,否则为新建一个键值对进行存储) sessionStorage.setItem('key', value)

本地存储复杂数据类型

  1. 方法 将复杂数据类型转化为 JSON 字符串,再存储在本地中

  2. 语法

    1. 将对象转化为 JSON 字符串 JSON.stringify(复杂数据类型)
    2. 存储数据 localStorage.setItem(key,JSON.stringify(复杂数据类型))
    3. 读取数据 JSON.parse(localStorage.getItem('key') 将JSON字符串转化为普通对象,此操作为深拷贝 ^78c9bb
  3. 举例

<script>
	const obj = {
		uname: '廖成林',
		age: 22,
		gender: '男'
	}
	// 复杂数据类型转化为 JSON 字符串并进行存储
	localStorage.setItem('obj', JSON.stringify(obj))
	// 读取数据并在控制台打印
	console.log(JSON.parse(localStorage.getItem('obj')))
</script>

#JSON字符串 属性和值都有引号,且是双引号 ,如上述例子的JSON 字符串为:{"uname":"廖成林","age":22,"gender":"男"}

Geolocation —— 获取设备定位

  1. navigation.geolocation.getCurrentPosition(success, error, options) 获取设备坐标,返回一个携带位置信息的 Promise ,success 和 error 都是函数

  2. navigation.geolocation.watchPosition() 注册一个位置监听器,位置改变时触发

  3. navigation.geolocation.clearWatch() 取消由 watchPosition() 注册的位置监听器

JS执行机制

  1. 特点

    1. 单线程,同一时间只能做一件事
    2. 所有任务都需要排队,前一个任务结束,后一个任务才会执行
  2. 问题 若 JS 执行时间过长,会造成页面渲染不连贯,导致页面渲染加载阻塞

  3. 解决 利用 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,于是 JS 出现了同步和异步

同步

  1. 含义 前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做法:先烧水煮饭,等水开了(10分钟)之后再去切菜,炒菜

  2. 同步代码 逐行执行,需原地等待结果后,才继续向下执行

异步

  1. 含义 多个任务可以同时处理,比如做饭的异步做法是,在烧水的同时,利用这10分钟去切菜,炒菜

  2. 异步代码 调用后耗时,不阻塞代码继续执行,在将来完成后触发一个回调函数

  3. 注意

    1. 同步和异步的本质区别是各任务的执行顺序不同

同步任务

  1. 解释 同步任务都在主线程上执行,形成一个执行栈

    ![[z_attachments/8740e8b4820aba8aff3cc033fe8c6a1.jpg|150]]

异步任务

  1. 实现方式 JS的异步是通过回调函数实现的

  2. 类型

    1. 普通事件,如 click , resize 等
    2. 资源加载,如 load, error 等
    3. 定时器, 如 setInterval , setTimeout 等
  3. 解释 异步任务相关添加到任务列队中(也称消息列队

    ![[z_attachments/fd6108abb4e18b2942911dd9d36d485.jpg|150]]

执行顺序

  1. 先执行执行栈中的同步任务
  2. 异步任务放入消息列队中
  3. 执行栈中的所有同步任务执行完毕后,系统依次读取任务列队中的异步任务,被读取的异步任务结束等待状态,进入执行栈,开始执行

![[z_attachments/84ae1a1eb92ca3d7fb2de56fa01dac9.jpg]]

[!tips] 注意

  1. 执行顺序为:同步代码 > 微任务(Promise) > 宏任务(定时器/事件)

事件循环

  1. 含义

    1. 由于主线程不断重复获取任务、执行任务,再获取任务、再执行任务,这种机制被称为事件循环(event loop)
    2. 执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
  2. 注意 获取异步任务时,谁先满足条件就先获取谁

![[z_attachments/1567e53db546d810a0a8df556520d10b.jpg|600]]

  1. 解释 由于JS是单线程的,无法同时处理主线程和任务列队,因此异步任务列队是由浏览器来处理的

![[z_attachments/Pasted image 20250626232530.png|600]]

[!tips] 注意

  1. 浏览器是多线程的,异步代码会被放到浏览器执行,执行完毕后推入任务队列排队
  1. 例子
// 打印结果为 111 222 444 333
console.log(111)
console.log(222)
// 即使延时为0,也会后执行,因为其回调函数是异步任务
setTimeout(function() {
console.log(333)
}, 0)
console.log(444)

常见同步异步

操作类型具体方法/操作同步/异步关键特性说明
定时器设置setTimeout()异步将回调推入任务队列,延迟执行
setInterval()异步周期性将回调推入任务队列
定时器清除clearTimeout()同步立即使定时器失效
clearInterval()同步立即停止周期性执行
事件监听添加addEventListener()同步立即注册监听器到元素
事件监听移除removeEventListener()同步立即从元素移除监听器
事件触发用户点击/键盘事件等异步通过事件循环异步处理
Promisenew Promise()同步构造函数同步执行
.then()/.catch()异步微任务队列执行
async/awaitasync函数声明同步函数本身同步创建
await表达式异步等待暂停执行,等待Promise解决
网络请求fetch()异步返回Promise,网络操作在后台执行
XMLHttpRequest.send()异步默认异步执行(可配置为同步但不推荐)
文件操作fs.readFile()(Node)异步通过线程池执行,回调异步处理
DOM操作element.appendChild()同步立即修改DOM结构
Console日志console.log()同步立即执行(但I/O延迟可能影响显示顺序)
Web Workersnew Worker()异步通信主线程与worker线程通过事件异步通信

[!tips] 注意

  1. 虽然定时器的设置是异步的,但其返回定时器 id 是同步的
  2. 判断一个事件是否同步,需要看它派发的操作,例如 unhandleinjection 事件、自定义事件和通过 JS 触发的事件(.click() .focus() .blur() .submit 等),它们直接向 window 或对应 dom 派发事件,不需要进入消息队列排队,而派发到消息队列的事件就是异步的

宏任务与微任务

  1. 含义 宏任务和微任务都是异步任务,ES6 之后引入了 Promise 对象,让 JS 引擎也可以发起异步任务
概念含义
宏任务浏览器环境执行的异步代码
微任务JS 引擎环境执行的异步代码
  1. 常见异步代码分类
宏任务(代码)执行所在环境
JS脚本执行事件(script)即 <script> 标签浏览器
setTimeout/setInterval浏览器
AJAX 请求完成事件浏览器
用户交互事件等浏览器
微任务(代码)执行所在环境注意
Promise对象.then()JS引擎Promise 本身是同步的,而 then 和 catch 回调函数是异步的
  1. 代码执行顺序

    1. 执行第一个 script 脚本事件宏任务,里面同步代码
    2. 遇到 宏任务/微任务 交给宿主环境,有结果回调函数进入对应队列
  2. 代码优先度 同步代码 > 微任务 > 宏任务

  3. 示例

console.log(1)
setTimeout(() => {
	console.log(2)
	const p = new Promise(resolve => resolve(3))
	p.then(result => console.log(result))
}, 0)
const p = new Promise(resolve => {
	setTimeout(() => {
		console.log(4);
	}, 0)
	resolve(5)
})
p.then(result => console.log(result))
const p2 = new Promise(resolve => resolve(6))
p2.then(result => console.log(result))
console.log(7);

// 1 7 5 6 2 3 4

![[z_attachments/a6f7f502fd13048288be5e3844c510b4.jpg]]

正则表达式

  1. 含义 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象

  2. 使用场景

    1. 验证表单 用户名表单只能输入英文字母、数字或下划线,昵称输入框中可以输入中文(匹配
    2. 过滤网页内容中的敏感词替换),或从字符串中获取我们想要的特定部分(提取
  3. 作用

    1. 表单验证(匹配
    2. 过滤敏感词(替换
    3. 提取字符串特定部分(提取
  4. 步骤

    1. 定义规则
    2. 根据规则查找:找到则返回

定义正则表达式

  1. 语法
const 变量名 = /表达式/
  1. 解释 /表达式/ 规则为包含表达式即匹配

  2. 注意

    1. /表达式/ 中的 / / 是正则表达式的字面量

正则表达式——匹配

  1. 判断是否有符合规则的字符 test() 方法,用来查看正则表达式与指定的字符串是否匹配

  2. 语法

// regObj 为规则对象,即下方的变量名
变量名.test(被检测的字符串)

正则表达式——替换

  1. 语法
// 替换一次
字符串.replace(/正则表达式/, '替换的文本')

// 全局查找并替换
字符串.replace(/正则表达式/g, '替换的文本')
  1. 注意
    1. 此方法只能替换一次,若要全局查找并替换可添加修饰符g

元字符

  1. 普通字符

    1. 定义 大多数字符仅能够描述它们本身,这些字符被称作普通字符,例如所有的字母、汉字和数字
    2. 注意 普通字符只能够匹配字符串中与它们相同的字符
  2. 元字符

    1. 含义 一些具有特殊含义的字符,可以极大提高正则的灵活性和具备强大的匹配功能
  3. 参考文档

    1. 正则表达式 - JavaScript | MDN
    2. 正则表达式在线测试工具
  4. 分类

类别含义
定位符表示位置,开头和结尾,规定必须用什么开头,什么结尾
量词表示重复次数
字符类如 \d 表示0-9
逻辑操作符|
转义符\
分组符()
捕获符()

边界符

  1. 作用 正则表达式中的边界符(位置符)用来提示字符所处的位置

  2. 分类

边界符说明示例
^匹配行首^哈 表示以哈为开头
$匹配行尾哈$ 表示以哈为结尾
  1. 注意
    1. 如果 ^$ 在一起,表示精确匹配\^二哈$\ 表示精确匹配“二哈”,只有“二哈”才会返回 true

量词

  1. 作用 表示设定某个模式出现的次数

  2. 分类

量词说明示例备注
*出现零次或更多次/哈*/ 表示匹配哈出现次数大于等于0的内容
+出现一次或更多次/哈*/ 表示匹配哈出现次数大于等于1的内容
?出现零次或一次/哈*/ 表示匹配哈出现次数等于0或1的内容
{n}出现n次/哈*/ 表示匹配哈出现次数等于n的内容
{n,}出现n次或更多次/哈*/ 表示匹配哈出现次数大于等于n的内容
{n,m}出现n到m次/哈*/ 表示匹配哈出现次数**在区间[n,m]**的内容{n,m} 逗号两侧不能添加空格
  1. 注意
    1. n为整数
    2. 量词匹配的不是一个片段内出现该词的总次数,而是出现该词的地方,该词的相连重复次数 量词匹配 /廖成林{2}/ 匹配 “廖成林廖成林”的时候输出结果为 true 匹配“廖成林2廖成林”时输出结果为 false 匹配 “廖成林2222廖成林廖成林”的时候输出结果为 true

字符类

  1. 分类
字符类说明示例
.匹配除换行符 (\n) 外的任意单个字符
[]匹配字符集合中的单个字符/[abc]/ 表示只要出现中括号中的
任意一个字符即返回 true
[a-z]匹配小写英文字母集合中的单个字符
[A-Z]匹配大写英文字母集合中的单个字符
[a-zA-Z]匹配大小写英文字母集合中的单个字符
[0-9]匹配数字0-9集合中的单个字符
[^][] 内的 ^ 表示此[]取反/[^0-9]/ 表示出现除了 0-9的任意
字符则返回 true
  1. 注意 /^[abc]$/.test('ab') 返回值为 false 的原因为,[] 只匹配集合中的单个字符,等价于 /^[a]$/ , /^[b]$//^[c]$/

    修改正则表达式为 /^[abc]{2}$/.test('ab') 返回值为 true

[] 支持诸如 [1-9] , [b-z] 等写法,要求在ASCII 编码中,左边的编码小,右边的编码大即可

  1. 正则匹配QQ号
// 腾讯QQ号从10000开始
^[1-9][0-9]{4,}$
预定义类 —— 正则
  1. 含义 指的是某些常见模式的简写方式

  2. 分类

预定义类说明
\b表示字符的边界,英文字母、数字、符号等的边界
/\bin/ 表示单词开头的in
\B匹配非字符边界的字符,如 /\Bi/ 表示匹配单词中间或结尾的i
\d匹配0-9之间的任一数字,相当于 [0-9]
\D匹配除0-9之间的任一字符,相当于 [^0-9]
\w匹配任一的字母、数字和下划线,相当于 [a-zA-Z0-9_]
\W匹配除所有字母、数字和下划线,相当于 [^a-zA-Z0-9_]
\s匹配空白字符(包括换行符、制表符、空格符等),相当于 [\t\r\n\v\f]
\S匹配非空白字符,相当于[^\t\r\n\v\f]
\f\n\r\t\v分别匹配换页符、换行符、回车符、水平制表符、垂直制表符

逻辑操作符

  1. 分类
逻辑操作符说明
|表示逻辑或
  1. 语法 /x|y/ 表示匹配包含x或y的内容

转义符

  1. 分类
转义符说明
\转义
  1. 注意
    1. \ + 普通字符 = 赋予特殊含义(如 \d 表示数字)
    2. \ + 特殊字符 = 取消特殊含义,按字面匹配(如 \. 匹配真正的点号)
    3. 注意双重转义: 在JavaScript等语言中用 new RegExp 创建正则时,字符串本身也会解释 \。要匹配一个反斜杠,正则模式需要 \\,但在字符串里要写成 "\\\\"!(如 new RegExp("\\\\") 匹配一个 \)

分组符

分组符说明
()把括起来的字符串当作一个整体

捕获符

捕获符说明
()划分捕获组
  1. 捕获组编号规则 兄弟关系从左到右编号 嵌套关系从外到内编号

  2. 捕获组

捕获组说明示例
(?<捕获组名>正则内容)命名捕获组(?<year>\d{4})
(?:正则内容)非捕获组,不编入捕获组(?:\d{2})
  1. 在正则表达式中引用之前的捕获组 通过 \n 的方式引用之前的捕获组, n 为捕获组编号
// 匹配重复单词
const regex = /(\b\w+\b)\s+\1/;
console.log(regex.test("hello hello")); // true
console.log(regex.test("hello world")); // false
  1. 直接使用 $n 引用捕获组进行替换
    1. 编号 $n 的 n 代表了捕获组的编号,编号从1开始,
    2. 语法
const newDate = "2023-05-01".replace(/(\d{4})-(\d{2})-(\d{2})/, "$2/$3/$1");
console.log(newDate); // "05/01/2023"

[!tips] 注意 整个正则捕获的内容可以用 $& 引用

  1. 通过索引访问捕获组
    1. 方式
      1. exec() 创建索引数组,通过s索引数组来访问
      2. match() 方法创建索引数组,通过s索引数组进行访问
    2. 语法
const text = "2023-05-01";
const regex = /(\d{4})-(\d{2})-(\d{2})/;

// 方式1: exec()
const result = regex.exec(text);
console.log(result[1]); // "2023"(年)
console.log(result[2]); // "05"(月)
console.log(result[3]); // "01"(日)

// 方式2: match()(无 g 标志时)
const match = text.match(regex);
console.log(match[1]); // "2023"

`` 3. 注意 1. exec() 方法返回的数组,第一项为检测的字符串 2. 索引数组的单元值为字符串型

  1. 命名捕获组
    1. 定义命名捕获组
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

`` 2.访问命名捕获组

const result = regex.exec("2023-05-01");

// 通过 groups 属性访问
console.log(result.groups.year);  // "2023"
console.log(result.groups.month); // "05"
console.log(result.groups.day);   // "01"

// 在替换中使用
const newDate = "2023-05-01".replace(
  regex, 
  "$<month>/$<day>/$<year>"
);
console.log(newDate); // "05/01/2023"

match 与 exec 详解

两者在不使用 g 修饰符时,行为几乎一致,[[详情见此|通过索引访问捕获组]] ,二者的详细区别如下

对比维度matchexec核心差异
所属对象String 的方法RegExp 的方法语法层面的本质区别
调用方式字符串.match(正则)正则.exec(字符串)主体和参数完全相反
无 /g 时返回第一个匹配的详细数组返回第一个匹配的详细数组效果几乎一致
有 /g 时返回所有匹配值的纯数组(只含匹配内容)单次返回第一个匹配的详细数组核心区别在此
信息保留有/g时丢失:捕获组、index、input始终保留:匹配值、捕获组、index、inputexec 信息更完整
全局匹配策略一次性获取所有匹配(简单直接)循环调用(依赖 lastIndex)exec 可控制匹配过程
捕获组获取有/g时无法获取循环中可获取所有匹配的捕获组exec 核心优势
适用场景只需匹配值,不需详细信息需要全局匹配 + 详细信息(捕获组/位置)需求决定选择
特性matchexec说明
非正则参数自动转为正则强制转为字符串match 更灵活
空匹配处理返回所有空匹配避免死循环exec 更安全
性能一次性处理,略快循环处理,灵活量少无感,量大考虑

使用全局匹配后,二者示例如下

// match (/g):简单直接,但信息有限
const str = "abc123def456";
const reg = /\d+/g;
console.log(str.match(reg)); // ['123', '456'] ← 纯数组

// exec (/g):信息完整,但需要循环
const reg2 = /(\d+)/g;
let result;
while ((result = reg2.exec(str)) !== null) {
    console.log(result); 
    // 每次输出详细数组:[匹配值, 捕获组, index, input...]
}

修饰符

  1. 含义 约束正则表达式的某些细节行为,如是否区分大小写,是否支持多行匹配等

  2. 语法

/表达式/修饰符
  1. 分类
修饰符说明
i单词 ignore 的缩写,正则匹配时字母不区分大小写
g单词 global 的缩写,匹配所有满足正则表达式的结果(全局匹配
m单词 multline 的缩写,多行查找,使 ^和$ 匹配每行边界
sdoAll ,点号通配模式,使 . 匹配包含 \n 在内的所有字符
uUnicode 模式,正确处理 Unicode 字符,如 /^\u{1F600}$/u 匹配 😀
y粘性匹配,从 lastIndex 开始匹配
  1. 注意

    1. 修饰符之间的顺序可以替换,没有影响
    2. Unicode 编码基本汉字范围为 \u4E00-\u9FFF (需在 u 模式下运行)
  2. 修饰符 y

const regex = /a/y;
const str = "a a a";

regex.lastIndex = 0;
regex.test(str);  // true (位置0)

regex.lastIndex = 1; 
regex.test(str);  // false (位置1是空格)
特性说明
强制从 lastIndex 开始匹配必须精确从当前 lastIndex 位置开始
禁止跳过字符不会像全局匹配那样搜索后续位置
匹配失败重置失败时自动将 lastIndex 重置为 0
匹配成功更新成功时 lastIndex 更新到匹配结束位置

贪婪匹配与懒惰匹配

  1. 改变方式 默认为贪婪匹配,在量词后面添加 ? 可以将匹配模式修改为懒惰匹配

  2. 贪婪匹配(默认)

    1. 含义 尽可能多匹配字符
    2. 示例 // 匹配规则 /at+/ //匹配字符(加粗) at atttt attttttt

![[z_attachments/Pasted image 20250628235702.png|300]]

  1. 懒惰匹配
    1. 含义 尽可能少匹配字符
    2. 示例 // 匹配规则 /at+?/ //匹配字符(加粗) at atttt attttttt

![[z_attachments/Pasted image 20250628235755.png|300]]

先行断言与后行断言

  1. 作用 匹配某些字符前面或者后面的内容,但不包括这些字符本身

正向先行断言

  1. 作用 匹配某些字符前面的内容

  2. 语法 /要匹配的内容(?=正则表达式)/

  3. 示例 // 匹配数字之前的 符号 `/\(?=\d+)/gm // 匹配成功的内容显示为代码格式 100`100 `` 200 $ abc

负向先行断言

  1. 作用 匹配除了某些字符前面的内容

  2. 语法 /要匹配的内容(?!正则表达式)/

  3. 示例 // 匹配数字之前的 符号 `/\(?!\d+)/gm // 匹配成功的内容显示为代码格式 $100 $200 $`abc

正向后行断言

  1. 作用 匹配除了某些字符后面的内容

  2. 语法 /(?<=正则表达式)要匹配的内容/

  3. 示例 // 匹配 符号之后的数字 `/(?<=\)\d+/gm // 匹配成功的内容显示为代码格式 $100 $200` $abc

负向后行断言

  1. 作用 匹配除了某些字符外后面的内容

  2. 语法 /(?<!正则表达式)要匹配的内容/

  3. 示例 // 匹配 符号之后的 `/(?<!\)[a-z]+/gm // 匹配成功的内容显示为代码格式 $100 $200 ¥abc`

作用域

  1. 含义 变量能够被范围的”范围“,包括局部作用域和全局作用域

[!tip] 注意

  1. 作用域是静态的,是函数写出来那一刻就确定的,因此看函数如何调用变量,查看它的位置就可以确定了
  2. 不同作用域可以声明同名变量,它们不会造成冲突的
let a = 1
function foo() {
  a++
}
function bar() {
  let a = 2
  foo()
  console.log(a)
}
bar()
console.log(a)
// 2 2

局部作用域

  1. 分类
    1. 函数作用域
    2. 块作用域

函数作用域

  1. 含义 在函数内部声明的变量只能在函数内部被访问,外部无法直接访问

  2. 注意

    1. 函数内部声明的变量在函数外部无法访问
    2. 函数的参数也是函数内部的局部变量
    3. 不同函数内部声明的变量无法相互访问
    4. 函数执行完毕后,函数内部的变量实际被清空了

块作用域

  1. 含义 使用 {} 包裹的代码叫做代码块,代码块内部声明的变量外部 有可能 无法被访问

  2. 注意

    1. let 声明的变量会产生块作用域, var 不会产生块作用域
    2. const 声明常量也会产生块作用域
    3. 不同代码块之间的变量无法相互访问
    4. 推荐使用 letconst

全局作用域

  1. 含义 <script> 标签和 .js 文件的最外层就是全局作用域,在此声明的变量在函数内部也可以被访问

  2. 作用 全局作用域中声明的变量,在其他任何作用域都可以被访问

  3. 注意

    1. windows 对象动态添加的属性默认是全局的,不推荐
    2. 函数中未使用任何关键字声明的为全局变量,不推荐
    3. 尽可能少声明全局变量,避免全局变量被污染

作用域链

[!tip] 注意

  1. 作用域链跟原型链有些行为上的相似处,但他们完全没有交集,作用域链用于查找变量,原型链用于查找属性
  1. 本质 作用域链本质上是底层的变量查找机制

  2. 查找规则 函数被执行时,会优先查找当前函数作用域中查找变量,如果当前作用域查找不到该变量,会依次逐级查找父级作用域,直到全局作用域

  3. 总结

    1. 嵌套关系的作用域串联起来形成了作用域链
    2. 相同作用域链中按照从小到大的规则查找变量
    3. 子作用域能够访问父作用域,父级作用域无法访问子级作用域

JS垃圾回收机制

  1. 垃圾回收机制(Garbage Collection) 简称 GC ,JS中的内存分配和回收都是自动完成的,内存不使用的时候会被垃圾回收器自动回收

  2. 生命周期

    1. 内存分配 声明变量、函数、对象时,系统会自动为他们分配内存
    2. 内存使用 即读写内存,也就是使用变量、函数等
    3. 内存回收 使用完毕,由垃圾回收器自动回收不再使用的内存
  3. 注意

    1. 全局变量一般不会回收(关闭页面回收)
    2. 一般情况下局部变量的值,不使用会被自动回收
  4. 内存泄漏 程序中分配的内存由于某种原因未释放或无法释放叫做内存泄漏

JS垃圾回收机制——算法说明

  1. 堆栈空间分配区别
    1. 栈(操作系统):由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放在栈里面
    2. 堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收,复杂数据类型放在堆里面
引用计数法
  1. 含义 IE 浏览器采用的是引用计数法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,如果没有引用了就回收对象

  2. 算法

    1. 跟踪记录被引用的次数
    2. 如果被引用了一次,那么就记录次数1,多次引用会累加++
    3. 如果减少一个引用就减1 --
    4. 如果引用次数为0,则释放内存
  3. 示例

// 创建一个堆空间,存储数据,并在栈空间中存储指向堆空间的地址
const arr = [0, 2, 5]
// 让栈空间中的地址为 null,此时数组[0, 2, 5]的引用为0,内存被释放
arr = null
  1. 缺陷 如果嵌套引用(循环引用),即两个对象相互引用,则会导致尽管他们已经不再使用,垃圾回收器也不会进行回收,导致内存泄漏 原因:嵌套引用的对象引用次数永远不为0,如果这样的引用大量存在会导致大量内存泄漏
function fn() {
	let o1 = {}
	let o2 = {}
	o1.a = o2
	o2.a = o1
	return '引用计数无法回收'
}
fn()
标记清除法
  1. 含义 现代浏览器通用的大多树基于标记清除算法的某些改进算法,总体思想是一致的

垃圾回收程序运行的时候,会标记内存中存储的所有变量(记住,标记方法有很多种)。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存

  1. 核心

    1. 标记清除算法将“不再使用的对象”定义为“无法到达的对象
    2. 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用
    3. 那些无法从根部触发触及到的对象被标记为不再使用,稍后进行回收
  2. 注意

    1. 函数,代码块等变量和对象从根部无法查找到,因此不管有没有嵌套引用,都会在使用后被清除

⭐闭包(closure)⭐

  1. 概念 一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域 即 闭包 = 内层函数 + 外层函数的变量

准确定义: 闭包 = 内层函数 + 词法环境,在 JS 中是指在函数作用域中的内层函数+词法环境,也可以理解为内层函数 + 外层函数的变量

如果变量特别大,而由于闭包的存在它并未被回收,就会导致在后台占用较大的内存,如果之后这个引用的值不被使用了,但是没有及时清除变量引用,就会造成内存泄漏

  1. 示例
// 函数 outer() 为闭包
function outer() {
	// 外层函数的变量
	let a = 10
	// 内层函数
	function fn() {
	console.log(a)
	}
	fn()
}
outer()
  1. 作用 封闭数据,提供操作,外部也可以访问函数内部的变量,但无法直接修改内部变量,从而实现数据的私有

  2. 闭包的核心

    • 函数保留对其词法作用域的引用:  当一个函数被定义在另一个函数内部时,它会将外部函数的作用域“打包”到自己的内部属性 [[Environment]] (或 [[Scopes]],具体实现细节由引擎决定) 中
    • 即使外部函数消亡  当外部函数执行完毕,如果内部函数(闭包)仍然存在(被全局变量、对象属性、事件监听器等引用),那么它携带的这个作用域引用也会继续存在。外部函数的局部变量不会被垃圾回收,因为它们还被闭包“惦记”着
  3. 闭包的基本格式

const fun = function outer() {
	let i = 1
	function fn() {
		console.log(i)
	}
	return fn
}
// 外层函数使用内部函数的变量
// 由于const fun = outer()这里的fun是全局变量,不会被垃圾回收机制回收,除非页面被关闭
const fun = outer()
fun() // 调用函数 fun
  1. 注意
    1. 由于闭包不会被自动垃圾回收机制回收,过度使用可能有内存占用过高(内存泄漏)的风险

变量提升

  1. 含义 是JS中的一种现象,它允许变量在声明之前即被访问(仅存在于 var 声明变量)

  2. 本质 在运行代码之前,JS会查找所有变量,将 var 声明的变量提升到该变量当前作用域最前面进行声明,但并不提升变量赋值

  3. 注意

    1. 变量在未声明之前被访问时会报语法错误
    2. 变量在var声明之前被访问,变量的值为 undefined
    3. let / const 声明的变量不存在变量提升
    4. 变量提升出现在当前作用域中
    5. 实际开发中推荐先声明再访问变量
  4. 示例

//打印 undefined
console.log(i);
var i = 1

// 上述代码发生了变量提升相当于如下代码
var i
console.log(i);
var i = 1


// 报错 不能在声明变量之前使用该变量
console.log(m);
let m = 1

// 报错 不能在声明变量之前使用该变量
console.log(n);
const n = 1

函数进阶

函数的自有属性

  1. 描述 函数是对象,每一个函数都有三个自有属性
function add () {}

// ['length', 'name', 'prototype']
console.log(Object.getOwnPropertyNames(add))
  1. 自有属性
属性名含义
length函数声明时形参的数量
name函数名
prototype函数的原型对象

new.target

  1. 含义 是一个指针,如果函数通过 new 关键字调用,它引用被调用的构造函数,反之它的值为 undefined,因此可以使用它来进行检测是否为构造函数调用

[!tip] 注意

  1. 这个属性只能在函数体内使用,否则会报错

默认参数作用域与暂时性死区

  1. 默认参数 函数可以为参数设定默认值,但是 arguments 并不会记录默认值,它只以调用时传递的值为准

  2. 定义多个参数默认值 如果定义了多个参数的默认值,那么会按照参数顺序执行初始化,后面的参数可以使用前面参数的值

  3. 暂时性死区 由于函数参数初始化的顺序,后定义的参数可以引用前面的参数,而前面的参数不能引用后面的参数,称为暂时性死区

  4. 参数位于一个独立的作用域中,它不能引用函数体的作用域

// 后面的参数可以引用前面的参数
function add (a = 0, b = a) {}

// 反过来引用,如果调用时为传递 a 的值,则会报错
function add (a = b, b = 0) {}

// 不能引用函数体的变量
function add (a = b, b = c) {
	let c = 'aaa'
}

函数提升

  1. 含义 与变量提升类似,函数提升只函数在声明之前即可被调用,本质是JS将函数声明提升至当前作用域最前面

  2. 示例

// 可以成功打印 123
fn()
function fn() {
console.log(`123`)
}

// 触发了函数提升,实际相当于如下代码
function fn() {
console.log(`123`)
}
fn()
  1. 与变量提升联动
// 函数表达式
fun()
var fun = function () {
console.log('123')
}

// 上述报错,提示fun不是一个函数,原因是,这里函数为赋值操作,而var只提升声明,不提升赋值,实际相当于
var fun
fun()
var fun = function () {
console.log('123')
}
  1. 注意
    1. 函数提升能够使函数声明和调用更加灵活
    2. 函数表达式不存在提升的现象
    3. 函数提升出现在相同作用域中

函数参数

  1. 组成 默认参数(形参和实参) 动态参数 剩余参数

动态参数(arguments)

  1. 含义 arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参

  2. 示例——利用动态参数构造求和函数

function getSum() {
	let sum = 0
	for (let i = 0; i < arguments.length; i++) {
		sum += arguments[i]
	}
	console.log(sum)
}
// 实参输入几个,就能求几个的和
getSum(1, 2, 3)
  1. 总结
    1. arguments 是一个伪数组,只存在于函数中
    2. arguments 的作用是动态获取函数的实参
    3. 可以通过for循环依次得到传递过来的实参

剩余参数

  1. 语法
function 函数名(...数组名) {
函数体
}
  1. 示例——使用剩余参数构造求和函数
function getSum(...arr) {
	let sum = 0
	for (let i = 0; i < arr.length; i++) {
		sum += arr[i]
	}
	console.log(sum)
}
// 实参输入几个,就能求几个的和
getSum(1, 2, 3)
  1. 示例——剩余函数进阶
function fn(x,...arr) {
	console.log(x) // 打印1
	console.log(arr) // 打印 [2, 3]
}
fn(1, 2, 3)
  1. 注意
    1. 剩余参数允许将一个不定数量的参数表示为一个数组
    2. ... 是语法符号,置于最末函数形参之前,用于获取多余的实参
    3. 借助 ... 获取的剩余实参,是个 ⭐真数组
    4. 开发中提倡多使用剩余参数

箭头函数

  1. 目的 引入箭头函数能够获得更简短的函数写法,并且不绑定 this ,箭头函数的语法比函数表达式更简洁

  2. 适用场景 箭头函数更适用于哪些本来需要匿名函数的地方

  3. 语法

// => 代表 function

// 箭头函数替代函数表达式
	// 原表达式
	const fn = function() {
		console.log(123)
	}
	fn()
	// 箭头函数替代
	const fn = () => {
	console.log(123)
	}
	fh()

// 只有一个形参时,可以省略小括号
	//打印 1
	const fn = x => {
	console.log(x)
	}
	fh(1)

// 只有一行代码时,可以省略大括号,
	// 打印 1
	const fn = x => console.log(x)
	fn(1)

// 只有一行代码时,此时函数的返回值为这一行代码,即 return x + x
	// 打印 1
	const fn = x => x + x
	console.log(fn(1))

// 直接返回对象
	const fn = uname => ({unmae: uname})
	fn('刘德华')
  1. 注意
    1. 直接返回对象时,由于对象的 {} 和函数的 {} 相同,只写一个会造成歧义,因此可以采用 ({对象属性}) 的方式消除歧义
    2. 箭头函数没有 arguments 动态参数,但是有剩余参数

this 的指向

执行上下文

  1. 描述 执行上下文包括全局执行上下文、函数执行上下文和 eval 执行上下文

<script type="module"> 标签很特殊,它的顶部 this 为 undefined ,不跟其他 script 标签共享全局上下文,这意味着,在这个 script 标签中 var a = 6 并不会给 window 添加 a 属性

  1. 概念明晰
概念描述
变量对象存储当前上下文中的变量和函数声明
活动对象函数上下文中的变量对象
作用域链用于变量查找的链式结构
调用栈管理执行上下文的栈结构
词法作用域函数在定义时确定的作用域
闭包函数能够记住并访问其词法作用域
维度全局执行上下文 (Global EC)函数执行上下文 (Function EC)Eval 执行上下文 (不推荐)
创建时机页面加载/脚本启动时,第一个 <script> 执行前创建每次函数调用时创建,包括构造函数、方法调用等执行 eval() 代码时创建,每次 eval 调用都会创建
存在数量唯一 (单例)。所有 <script> 标签共享同一个全局上下文无数个。每次函数调用都创建新的,即使是同一个函数视 eval() 调用次数而定,每次调用创建独立上下文
生命周期伴随页面整个生命周期,页面关闭/刷新才销毁函数执行完毕后通常销毁 (闭包情况除外)eval() 代码执行完即销毁,但可能影响外部作用域
包含内容全局对象 (window/global)、全局变量、全局函数声明arguments 对象、局部变量、内部函数声明、参数动态创建的变量和函数,可以访问调用位置的作用域
作用域链链条的顶端 (Global Scope),只有全局作用域当前函数作用域 → 父级作用域 → ... → 全局作用域继承调用位置的作用域链 + 自身作用域,动态生成
This 指向浏览器中指向 window;Node 中:
• 文件顶层指向 module.exports
• REPL 环境指向 global
取决于调用方式:
• 普通调用:严格模式 undefined,非严格模式 window/global
• 方法调用:调用对象
• new 调用:新创建的对象
• call/apply/bind:指定对象
默认指向调用者的 this (非严格模式),严格模式下为 undefined

this 指向

  1. 描述 普通函数的 this 指向是动态的,在调用时才确定,共有四种,默认绑定,隐式绑定、显式绑定、new 绑定,其中优先级为 new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定
// 如何判断为哪种绑定,优先级从高往低看,如果前面三个都没有,就是默认绑定

箭头函数的没有自己的 this ,它向上寻找最近执行上下文,并将其 this 作为自己的 this

优先级规则名称代码示例This 指向备注
最高箭头函数() => {}外层作用域的 this无法被修改 。 new 、 call 、 bind 对其无效。
1new 绑定new Fn()新创建的实例对象构造函数内部的 this 被锁定到新实例上。
2显式绑定fn.call(obj) fn.apply(obj) fn.bind(obj)()参数 obj指名道姓。 bind 是强绑定 (Hard Binding)。
3隐式绑定obj.fn()obj (上下文对象)容易发生 隐式丢失 (赋值给变量或回调传参)。
4默认绑定fn()window (非严格) undefined (严格)独立函数调用。包含自执行函数 IIFE。
特殊DOM 事件el.onclick = fnel (绑定事件的元素)类似于隐式绑定。
特殊Script 标签<script>console.log(this)</script>window处于全局上下文中。
  1. bind 的特殊点 call , apply 都是讲 this 绑定到指定对象后运行一次函数,这个绑定是临时的,运行完之后,原函数的 this 并没有被修改为 call 或者 apply 的第一个参数

而 bind 不同,它并不会运行函数被改变 this 指向的函数,它返回一个新函数,这个新函数的 this 被永久改变了,具体操作如下

// 对于 fn 而言
function fn() { console.log(this) }
const obj = { a: 1 }

function bind(thisArg, ...args) {
  if (通过 new 操作符调用new.target) {
    return new fn()
  }
  else {
  // this 指向被永久改变了
    return function (thisArg, ...args) {
     fn.call(thisArg, ...args)
    }
  }
}
fn.bind(obj)() // { a: 1 }

一个易错点

[!tip] 只要不是在函数 Foo 的 { ... } 内部 (包括参数列表) 被创建/声明 的,它就不属于 Foo 的内部作用域

因为严格来说 fn(() => this) 中的 this ,是箭头函数的 this ,而这个箭头函数并没有定义在 fn 的作用域内部,它相当于定义在外面,然后作为一个参数传入 fn ,由于它定义在 fn 外面,因此 this 因寻找外层的函数上下文 this ,如果没有,就为 globalThis

因此,下面的 this 虽然作为 new Promise 的参数,但它不在 Promise 构造函数作用域内,因此这个箭头函数 this 继承的是 fn 的 this

function fn () {
  // 必须把 Promise 返回出去,否则外面拿到的是 undefined
  return new Promise((resolve, reject) => {
    // 在 Promise 构造器里,return 是没用的,必须用 resolve 把值传出去
    resolve(this)
  })
}
const obj = { a: fn }
// 因为返回的是 Promise,所以要用 .then 查看结果
obj.a().then(res => console.log(res))

扩展操作符(展开运算符)(...)

  1. 作用 将一个数组、对象或字符串进行展开,会展开对象的可枚举属性,数组当作对象展开,字符串按索引展开,其他情况下,相当于没有

  2. 示例——无效情况

// 以下用法相当于没有这行代码
...null
...true
...false
...undefined
// 数字类型
...123

对数组

  1. 语法
const arr = [1, 5, 3, 6, 8]
// 扩展操作符(展开运算符)展开函数控制台输出结果为 1 5 3 6 8
console.log(...arr)
  1. 注意

    1. 扩展操作符(展开运算符)不会修改原数组
  2. 运用场景 求数组最大值(最小值)、合并数组等

const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
// 求数组最大值
console.log(Math.max(...arr1))
// 求数组最小值
console.log(Math.min(...arr2))

// 合并数组
const arr = [...arr1, ...arr2]

在上述代码中可知, ...arr 等价于 1, 2, 3

对对象

  1. 复制对象可枚举属性
const obj = { a: 1, b: 2 };
const copy = { ...obj }; // { a: 1, b: 2 }
  1. 合并多个对象 后者会覆盖前者的同名属性
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
  1. 添加/覆盖属性 可结合新属性使用,同名时右侧优先级更高
const updated = { ...obj, b: 99, d: 100 }; 
// { a: 1, b: 99, d: 100 } (覆盖 b,新增 d)

解构

  1. 分类
    1. 数组解构
    2. 对象解构

[!tips] 注意

  1. 结构时使用的关键字决定了解构出来的量的特性,如 let 关键字解构出来是变量, const 是常量
  2. 解构出来的是新的变量解构是浅拷贝

解构数组

  1. 含义 是将一系列单元值快速批量赋值给一系列变量的简洁语法

  1. 基本语法
    1. 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元格将被赋值给左侧的变量
    2. 变量的顺序对应数组单元值的位置依次进行赋值操作
// 普通数组
const arr = [1, 2, 3]

//批量声明变量 a, b, c ,同时将数组单元值 1 2 3 依次赋值为变量 a b c
const [a, b, c] = arr
console.log(a) // 1
console.log(a) // 2
console.log(a) // 3

  1. 应用场景
    1. 两个变量相互交换
let a = 1
let b = 2
// 交换,前面必须加 ; 要不然会报错
;[b, a] = [a, b]

  1. 特殊情况——变量数量 > 单元值数量 多出来的变量会返回 undefined ,类似于 [[#^37dca5|形参 > 实参]]
const [a, b, c, d] = [1, 2, 3]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
console.log(d) // undefined

  1. 特殊情况——变量数量 < 单元值数量 使用剩余参数将多余单元值作为数组保存,剩余参数只能放在最末位
const [a, b, ...c] = [1, 2, 3, 4]
console.log(a) // 1
console.log(b) // 2
console.log(c) // [3, 4]

  1. 防止 undefined 传递 通过设置默认值防止 undefined 传递 允许初始化变量的默认值,且只有单元值为 undefined 时默认值才会生效
// d = 0, e = 0 为设置默认值
const [d = 0, e = 0] = []
console.log(e); // 0
console.log(d); // 0

  1. 按需导入,忽略某些值
// 苹果被忽略
const [a, , c, d] = ['小米', '苹果', '华为', ‘格力']
console.log(a) // 小米
console.log(c) // 华为
console.log(d) // 格力

  1. 支持多维数组的解构
// 二维数组
const arr = [1, 2, [3, 4], 5]

// 打印二维数组中的数据 4
console.log(arr[2][1]) // 4

// 解构二维数组
const [a, b, c, d] = arr
console.log(a) // 1
console.log(b) // 2
console.log(c) // [3, 4]

// 多次解构二维数组
const [a, b, [c, d]] = arr
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
console.log(d) // 4

解构对象

  1. 含义 对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法

  2. 基本语法

    1. 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
    2. 对象属性的值将被赋值给与属性名相同的变量(因为对象是无序的,不能像数组一样变量和单元值一一对应,因此只能通过变量名与属性名相等来匹配)
    3. 注意解构的变量名不要和外面的变量名相冲突,否则会报错
    4. 对象中找不到与变量名一致的属性时变量赋值为 undefined
    5. 解构时可以赋默认值,默认值只有传递 undefined 时才会生效
const { uname, age } = { uname: ’廖成林', age: 22 }
  1. 特殊情况——外部变量名与对象属性名冲突报错 通过修改解构变量名解决 冲突变量名: 新变量名
const uname = '廖成林'
// 修改解构变量名
const { uname: username, age } = { uname: ’廖成林', age: 22 }
console.log(username) // 廖成林
  1. 特殊情况——解构数组对象
const pig = [
	{
		uname: '佩奇',
		age: 6
	}
]

//先解构数组,再解构对象
const [ {uname, age }] = pig
console.log(uname) //佩奇
  1. 特殊情况——多级对象解构
const pig = {
	uname: '佩奇',
	family: {
		mother: '猪妈妈',
		father: '猪爸爸',
		sister: '乔治'
	},
	age: 6
}

// 解构
const {uname, family: { mother, father, sister}, age } = pig
console.log(mother) //猪妈妈
  1. 特殊情况——数组对象解构
const person = [
	{
		uname: '佩奇',
		family: {
			mother: '猪妈妈',
			father: '猪爸爸',
			sister: '乔治'
		},
		age: 6
	}
]

//解构数组对象
const [{ uname,, family: { mother, father, sister}, age }] = person
console.log(mother)

剩余操作符 —— 将剩余的可枚举属性收集到一个对象中

  1. 示例
const obj1 = {
	name: 'lcl',
	age: '22',
	love: 'dhh',
	birthday: '2003/02/20'
}
const { name: lclName, age, ...obj2 } = obj1

console.log(lclName) // 'lcl'
console.log(obj2)
// {
	// love: 'dhh',
	// 'birthday': '2003/02/20'
// }