JavaScript高级程序设计(第5版):无处不在的集合

每个系列一本前端好书,帮你轻松学重点。

本系列来自曾供职于Google的知名前端技术专家马特·弗里斯比 编写的 《JavaScript高级程序设计》(第5版)

世界上很少有事物孤立存在,即便你是单身,世界上有很多单身...

所以,集合(列表)是一种很常见的数据形式,新闻列表、人员名单列表等。

本篇文介绍JavaScript中的这类数据。

集合的共性

你一眼就能看出什么样的数据是集合,就像这样:

复制代码
1,2,3,4,5
张三,李四,王五

所以必然存在共性,如:包含多个同类型的值(即便可以不同),能够遍历、增加、删除、查找、修改。

但往往因为集合类型的不同,适用场景也不同。

数组

数组是最常见的集合类型,不仅JavaScript,其他语言同样,但在JavaScript中,它有其特殊性,它的每个索引位可以存储不同类型的数据,而且是动态大小,会随着数据增加自动增长。

数组定义

定义数组有若干方法,以下都是可行的:

javascript 复制代码
let colors = new Array();  // 不知数量
let colors = new Array(20); //  已知数量
let colors = new Array("red","blue");  // 直接传入元素
let colors = ["red","blue"]; // 字面量法

除此之外,还有两个方法,可以将看起来像数组,但本身不是数组的数据转换成数组,就是from()、of()。

什么叫"像而不是",比如类数组,当我们通过DOM API document.getElementsByTagName()等获取到一组NodeList,就是类数组。

类数组只具备一部分数组的能力,有length属性,可通过索引访问元素,但不能正常使用数组的方法,如果需要,就可以用Array.from()方法将其转换为数组再使用。

of()方法则用于将一组参数转换为数组,如 Array.of(1,2,3,4),结果是 [1,2,3,4]。

数组方法

数组如此常用,肯定存在很多情况需要处理,于是JavaScript赋予了它一众强大的方法,这些方法为实际项目中各种需求提供了便利,此处介绍一部分。

数组项方法

less 复制代码
let colors = ["red","blue"]; // 原本是这样
colors.push("yellow")
["red","blue","yellow"];  // 操作后

除此之外,还有shift(取第一项)、unshift(头部添加)、pop(取最后一项)

迭代方法

包括forEach、map、filter等,它们对数组的每一项运行其中的函数体,并根据各自的能力返回相应结果。

forEach遍历数组项,没有返回值,类似for循环。

map返回代码逻辑中指定的返回值,组成新的数组。

filter返回代码中条件判断为true的数组项,组成新的数组。

dart 复制代码
let numbers = [2,1,3]
let newNumbers = numbers.map(num=>{
	return num + 1
})
// [3,2,4]
let newNumbers = numbers.filter(num=>{
	return num > 1
})
// [2,3]

除以上方法外,数组还有很多其他实用方法,如"reverse(反转)、sort(排序)、reduce(归并)、includes(包含)、flat(展开)"等,相当丰富,可满足各种需求,篇幅原因不一一介绍,各位根据需要拓展学习。

笔者在最初学习时,曾苦于方法过多难以记忆,现在告诉你,不用记,没有比实践带来的印象更深,用到的时候多查,用多了自然记住。

定型数组

JavaScript中本没有定型数组(Typed Array),它的行为也与普通数组有所差异,它是什么,又为何存在?

定型数组指的是一种特殊的包含数值类型的数组。设计的目的是提升向原生库传输数据的效率,如:WebGL。

就是说,JavaScript会与WebGL产生交互,但二者的数据格式不同,需要额外处理,这就带来更多消耗。

当你看到Float32Array,Int32Array、ArrayBuffer、DateView等略感陌生的数据时,就代表定型数组出现了。

ArrayBuffer是所有定型数组的基本单位,当使用WebAssembly时,可能见到ShareArrayBuffer,它是ArrayBuffer的一种变体。

定型数组的应用尚不广泛,初步了解这就够了。

Map

Map是ES6中加入的新的集合类型,它带来了真正的键-值存储机制。

什么叫"真正",还有假的?

很长一段时间内,这个角色都由Object在扮演。

编码的灵活性决定了一些代码的功能可用另一种替代,这就是典型的例子,Map的多数特性Object都可以实现,但是专一职责原则又告诉我们它该有更确切的实现。

基本API

dart 复制代码
const m = new Map();
m.set("name","说书匠").set("age",18);
m.has("name"); // true
m.get("name"); // "说书匠"
m.size   // 2
m.delete("age")  // age被删
m.clear();  // map清空

由上可见,Map中添加、判断和查找键值是很直观的,你或许发现了第二行的特殊之处,set后面又跟了个set,因为set本身会返回映射的实例,就又可以接着用了。

与Object只能用数值、字符串或符号作为键不同,Map可以使用任意JavaScript数据类型作为键,值也没有限制。

除此之外,在迭代方面也有差异,Map实例会维护键-值对的插入顺序,可以根据插入顺序执行迭代操作。可用的方法有keys()、values()、entries()等。

它们对于内存占用的情况也不同,固定内存情况下,Map可以比Object多存储50%左右的键-值对。

在插入、查找、删除等操作的性能上,Map也比Object更优,所以,需要用到键-值对的时候,我们可以调整一下编码习惯,优先选择Map。

Set

Set是另一种新增的集合型数据结构,像加强版的Map,因为它们多数API是相似的,正因如此,学习起来较为轻松。

基本API

scss 复制代码
const m = new Set([1,2,3])
m.size  // 3
m.add(4).add(5)
m.has(3)  // true 
m.delete(1)
m.clear()

操作同样直观易懂,不赘述。

既然是集合,也能迭代,Set的迭代也是支持顺序的,同样支持keys()、values()、entries()等。

其实,从形式上看,Set更像Array,没有键,只有值,那么这两者间又该如何选用呢?

每种数据结构的选择都应有原因,不妨做个对比。

适合Set场景:

1、需要存储不重复的唯一值

2、频繁检查元素是否存在

3、需要数学集合运算

4、需要频繁增删

适合Array场景:

1、需要有序

2、索引访问。

必要时,二者还可相互转换:

sql 复制代码
// Array转Set
const m = new Set([1,2,3])
// Set转Array
[...m]

最后这个"..."操作是什么?

三个点也是一种操作符,叫"扩展"操作符,它可用于普通的对象、数组和Set这种集合类数据中,相当于把后面变量包含的值放到当前"容器"中。

同时,集合类型的数据都支持 for-of 循环,对于迭代访问每个值就方便多了。

弱引用

这是与JavaScript中垃圾回收相关的一个机制,我们定义的变量,引擎会自动管理它们占的内存,通常来说,当某个对象不再使用,垃圾回收器会释放它占的内存。

如果我们想保持对对象的引用,同时不阻止它被回收的行为,这种引用就称为"弱引用"。

弱引用的创建方式有三种:WeakRef、WeakMap、WeakSet 。分别对应Object、Map和Set

除了不阻止垃圾回收,它们在特性和操作上跟前面介绍的也有区别。

WeakMap和WeakSet中的键只能是Object或者继承自Object的类型,同时,它们不可迭代,没有clear()方法。

小结

集合类型之常见,每个项目,甚至每个文件,都会有它们的身影,其中以数组最为常用,所以,掌握它们是必须的。

但正如前文所述,集合在某种程度上是对数据的存储和管理,像载体,最终,我们还是要拿到具体的每个值去做分析和处理,这时候,增删改,迭代方法,就很重要了,恰恰JavaScript提供了非常丰富又实用的方法,希望大家多加练习,熟练掌握,在开发时就能游刃有余。

更多好文第一时间接收,可关注公众号:"前端说书匠"

相关推荐
kite01212 小时前
浏览器工作原理06 [#]渲染流程(下):HTML、CSS和JavaScript是如何变成页面的
javascript·css·html
крон2 小时前
【Auto.js例程】华为备忘录导出到其他手机
开发语言·javascript·智能手机
coding随想4 小时前
JavaScript ES6 解构:优雅提取数据的艺术
前端·javascript·es6
年老体衰按不动键盘4 小时前
快速部署和启动Vue3项目
java·javascript·vue
小小小小宇4 小时前
一个小小的柯里化函数
前端
小小小小宇4 小时前
前端双Token机制无感刷新
前端
小小小小宇4 小时前
重提React闭包陷阱
前端
小小小小宇4 小时前
前端XSS和CSRF以及CSP
前端
UFIT4 小时前
NoSQL之redis哨兵
java·前端·算法