JavaScript 数组
对于需要收集的 有序、具有索引的数据 的需求,可以使用数组
数组使用过程中要注意的细节
数组的索引其实是数字为名称的特性,空项目则是连数字特性都不存在。
1 | var array1 = [undefined, undefined] |
建议不要修改数组的 length, 也不要让数组产生空项目, JavaScript 中的数组 API 对数组空项处理方式不同
map/filter/forEach 都会跳过空项,回调函数不会执行。但返回结果不一样,filter 不保留空项,map 会保留空项
过滤除 undefined ,除 0,false,null,’’ 等
undefined 不是保留字,避免 undefined 被拿来当变量名称设定了其他的值,所以使用 typeof 确认值的类型名称
1 | function ifUndefined(obj, name) { |
数组的静态方法
是以 Array 为命名空间的方法,包括以下几个:
Array.isArray()
Array.isArray() 的判断依据是数组内部实现特性[[Class]]的值’Array’
1 | class SubArray extends Array {} |
用类语法继承,子类实例会被 Array.isArray() 判断为 true
类语法的继承能继承标准 API,而且内部实现特性以及特殊行为也会继承
1 | let obj = {} |
对象原型被修改的类数组,可以骗过 instanceof 但 Array.isArray() 判断为 false
Array.of()
1 | var arr = new Array(10) // 创建一个长度为 10 的稀疏数组 |
以上 demo 通过 Array 构造函数创建了数据,参数两层函数,一是数组的长度。二是数组的元素。较混乱
常用字面量形式定义数组,方便,容易理解
可使用 Array.of() 建立指定元素数组
1 | Array.of(10) // 建立一个元素为 10 的数组 [10] |
Uncaught SyntaxError: Unexpected token ‘,’ 异常说明 与字面定义数组语法不同[1, 2, , 4],Array.of() 不支持空项目
1 | class SubArray extends Array{} |
Array.from()
Array.from() 可以接受类数组或可迭代对象,传回的新数组包含类数组或可迭代对象元素
Array.form() 从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例
1 | Array.from('helen') // (5) ["h", "e", "l", "e", "n"] |
改变数组
API | description |
---|---|
sort | 默认排序方式是按照 Unicode 码点,若元素不是字符串,会先转换为字符串再排序。 若要按指定方式排序,必须传入带有两个参数的回调函数,传回的值决定了排序方式。结果改变原数组,如下 demo —— sort 排序 |
reverse | 反转数组后传回原数组 结果改变原数组, 如下 demo —— reverse 排序 |
fill | 用某个值进行填充。三个参数:一是要填充的值,二是 start 填充值的起始索引;三是 end 填充值的结束索引。**可以直接使用 call() 或 apply() 指定 fill() 调用时的 this 对象。如下 demo —— fill 的使用 |
1 | // sort 排序 |
实现堆栈
API | 描述 |
---|---|
push | 可接受一个或多个参数,接收的参数追加至数组结尾。返回数组长度。改变原数组 |
pop | 移除并返回数组的最后一个元素 |
1 | // push |
push + pop 可视为先进后出的堆栈结构使用
实现队列
API | 描述 |
---|---|
unshift | 在数组的前端插入一个或多个元素,返回数组长度 |
shift | 移除数组前端的第一个元素 |
1 | let queue = [] |
unshift + ship 可视为先进先出的队列
有关数组 API push/pop/fill 的几个扩展 demo
Array.prototype 定义的方法,都具有通用性,可以对普通的对象进行操作,操作结果会令指定的对象成为类数组,必要时,也可以直接将对象的原型设为 Array.prototype
1 | let o = {} |
函数式风格 API
有关函数式程序设计
API | 描述 |
---|---|
indexOf | 查找元素所在的第一个索引,返回 -1 代表没有该元素 |
lastIndexOf | 查找元素所在的最后一个索引,返回 -1 代表没有该元素 |
includes | 查找数组中是否有指定元素 ES7,返回值为 true/false |
find | 查找符合条件的元素,参数可以是一个回调函数。ES6 |
findIndex | 查找符合条件的元素的索引,参数同样可以是一个回调函数。ES6 |
filter | 过滤出符合条件的元素,返回的是一个数组 |
slice | 数组切割,参数一,切割起始索引;参数二切割终止索引,若无,默认 array.length。不改变原数组,返回新的数组 |
concat | 数组拼接,不改变原数组。返回拼接后的数组 |
every | 判断数组是否都符合某个条件 |
some | 判断数组中是否有符合条件的元素 |
join | 使用字符串将数组串接为字符串 |
flat | 二维数组摊平成一维数组(ES10) |
flatMap | 推平数组的同时进行数组元素的转换,接受回调函数,执行后返回一维数组 |
reduce | 处理结果为单一的值,逐一削减数组,最终拿到单一值 |
reduceRight | 从右向左迭代元素 |
以上三个 api 都是使用 === 来判断是否存在该元素
NaN !== NaN NaN 不等于任何值
查找数组中的 NaN
1 | function indexOf(arrays, value, idx = 0) { |
* 纯函数式编程,**不提供循环语法**,使用递归解决重复性任务。
* 纯函数式编程中,没有变量的概念,也不能改变对象或数据状态。
1 | // 递归1——思考边界条件 |
群集
Set 与 WeakSet
使用 Set 收集不重复的值,Set 构造函数接收可迭代对象
API | 描述 |
---|---|
String.prototype.split | 将字符串按照指定字符进行切割,返回数组 |
Set.prototype.size | 得到 Set 集合中元素的数量 |
Set.prototype.values | 返回一个新的迭代对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值 |
Set.prototype.add | 在 Set 集合尾部添加一个元素,返回该 Set 对象 |
Set.prototype.has | 返回一个布尔值,表示该值在 Set 中是否存在 |
Set.prototype.delete | 移除 Set 中与参数相等的元素 |
Set.prototype.clear | 移除 Set 对象中的所有元素 |
1 | // 收集以下字符串中不重复的值 |
set 本身无序,不具备索引。没有直接可取的 get 之类的方法,有关 Set 方法,见上表
ECMAScript 对 Set 采用 SameValueZero 演算 相等采用 ===,0 等于 -0, Set 中若有 NaN,试图再加入 NaN,Set 中还是只有一个 NaN
1 | new Set([NaN,NaN,10,10,1,undefined,undefined]) // Set(4){NaN, 10, 1, undefined} |
ES6 中也提供了 WeaKSet,只能加入对象,不能加入除对象以外的,如 null,undefined,NaN和一些基本类型;垃圾回收时不会考虑是否被 WeakSet
管理,只要对象没有其它名称参考着,就会回收。避免必须使用 Set 管理对象,却忘了从 Set 中清除对象而发生内存泄漏。由于对象没有引用就会被回收,所以WeakSet 没有 size,只有 add(),delete(),has()
方法
Map 与 WeakMap
历史:JavaScript 对象是键值对,键只能是字符串,不可以使用字符串以外的类型
现在:ES6 提供的 Map 类型,键可以使用基本类型、undefined、NaN、null 与对象,Map 对键的唯一性采用的是 SameValueZero 演算
Map 的实例是可迭代对象,使用 for…of 迭代出的元素会是个包含键与值的数组,使用解构语法可以分解成
API | 描述 |
---|---|
Map.prototype.size | 返回 Map 对象的键/值 |
Map.prototype.has | 返回布尔值,表示 Map 实例是否包含键对应的值 |
Map.prototype.clear | 清除 Map |
Map.prototype.keys | 返回一个新的 Iterator 对象,插入顺序包含了Map对象中每个元素的键 |
1 | // 过去 |
ES10 新增了 Object.fromEntries() 函数,建构对象时使用,接受可迭代物(如,数组、Map)作自变量,该对象迭代出来的每个元素都必须是[键,值]形式
1 | Object.fromEntries([['k1','v1'],['k2','v2']]) // {k1: "v1", k2: "v2"} |
WeakMap 只能使用除 null 以外的对象作为,垃圾回收时不会考虑对象是否被 WeakMap,只要对象没有被引用就会被回收。WeakMap
不存在对象作为键,值也会被清除。可以用来避免从Map中清除对象而发生内存泄漏
数组重组方法
1. 数组转换成字符串
- Array.prototype.toString() 将数组转换成 以逗号分隔的字符串
1
2
3var arr = ["cookie", "local storage", "session storage", "web storage"];
arr.toString();
// "cookie,local storage,session storage,web storage" - Array.prototype.join([separator]) 将数组转换成 指定分隔符的字符串
1
2arr.join('/')
// "cookie/local storage/session storage/web storage" - Array.prototype.concat() 合并两个以上数组,最终拿到生成后的新数组,生成的新数组是需要合并的数组的浅拷贝。
注意:
如果将引用类型复制到数组中,引用类型改变,新的数组数组发生改变,反之一样。因为他们指向同一个引用对象。1
2
3
4
5
6
7
8var arr_reference_type = [['concat'],'array prototype function'];
var arr_concat = arr.concat(arr_reference_type);
console.log(arr_concat);
// ["cookie", "local storage", "session storage", "web storage", ["concat"], "array prototype function"]
// 修改 arr_reference_type
arr_reference_type[0].push('slice');
console.log(arr_concat);
// ["cookie", "local storage", "session storage", "web storage", ["concat", "slice"], "array prototype function"]