本文简单介绍了一些常用的JS数组操作,内容较为零散。

基本操作

at

有些编程语言允许我们使用负数索引来实现获取数组的最后一个元素,但这在JS中行不通,在JS中,我们可以使用at:

1
2
3
4
let fruits = ["Apple", "Orange", "Plum"];

// 与 fruits[fruits.length-1] 相同
alert( fruits.at(-1) ); // Plum

换句话说,arr.at(i)

  • 如果 i >= 0,则与 arr[i] 完全相同。
  • 对于 i 为负数的情况,它则从数组的尾部向前数。

增添元素

  • push(...items) 在末端添加 items 项。
  • pop() 从末端移除并返回该元素。
  • shift() 从首端移除并返回该元素。
  • unshift(...items) 从首端添加 items 项。

splice

1
arr.splice(start[, deleteCount, elem1, ..., elemN])

从索引 start 开始修改 arr:删除 deleteCount 个元素并在当前位置插入 elem1, ..., elemN。最后返回被删除的元素所组成的数组。

1
2
3
4
arr.splice(1, 1); // 从索引 1 开始删除 1 个元素
arr.splice(0, 3, "Let's", "dance");
arr.splice(0,arr.length);
arr.splice(-1, 0, 3, 4);

slice

1
arr.slice([start], [end])

它会返回一个新数组,将所有从索引 startend(不包括 end)的数组项复制到一个新的数组。startend 都可以是负数,在这种情况下,从末尾计算索引。

我们也可以不带参数地调用它:arr.slice() 会创建一个 arr 的副本。其通常用于获取副本,以进行不影响原始数组的进一步转换。

concat

1
arr.concat(arg1, arg2...)

它接受任意数量的参数 —— 数组或值都可以。

结果是一个包含来自于 arr,然后是 arg1arg2 的元素的新数组。

1
2
let arr = [1, 2];
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6

搜索

find

寻找一个匹配的元素

1
2
3
4
let result = arr.find(function(item, index, array) {
// 如果返回 true,则返回 item 并停止迭代
// 对于假值(falsy)的情况,则返回 undefined
});

还有findIndexfindLastIndex返回匹配索引。

filter

寻找所有匹配的元素

1
2
3
4
let results = arr.filter(function(item, index, array) {
// 如果 true item 被 push 到 results,迭代继续
// 如果什么都没找到,则返回空数组
});

转换

map

最常用的方法,对每个元素都调用函数,然后返回结果数组。

1
2
3
let result = arr.map(function(item, index, array) {
// 返回新值而不是当前元素
})

sort

对原数组进行排序。

1
arr.sort( (a, b) => a - b );

split/join

1
2
3
4
5
6
7
let names = 'Bilbo, Gandalf, Nazgul';

let arr = names.split(', ');

for (let name of arr) {
alert( `A message to ${name}.` ); // A message to Bilbo(和其他名字)
}
1
2
3
4
5
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];

let str = arr.join(';'); // 使用分号 ; 将数组粘合成字符串

alert( str ); // Bilbo;Gandalf;Nazgul

Array.isArray

Array.isArray(value) 检查 value 是否是一个数组,如果是则返回 true,否则返回 false

数组的对象本质

在JS中只有8中基本数据类型,数组实际上是一种对象,真正特殊的是它们的内部实现。JavaScript 引擎尝试把这些元素一个接一个地存储在连续的内存区域,而且还有一些其它的优化,以使数组运行得非常快。但是,如果我们不像“有序集合”那样使用数组,而是像常规对象那样使用数组,这些就都不生效了。

下面的操作会使数组退化为对象:

1
2
3
4
5
let fruits = []; // 创建一个数组

fruits[99999] = 5; // 分配索引远大于数组长度的属性

fruits.age = 25; // 创建一个具有任意名称的属性

可迭代对象

为对象添加一个名为 Symbol.iterator 的方法(一个专门用于使对象可迭代的内建 symbol)。

这个方法需要返回一个有next方法的对象,其返回的结果格式必须为 {done: Boolean, value: any},当 done=true 时,表示循环结束,否则 value 是下一个值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let range = {
from: 1,
to: 5,

[Symbol.iterator]() {
this.current = this.from;
return this;
},

next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};

for (let num of range) {
alert(num); // 1, 然后是 2, 3, 4, 5
}

对于可迭代或类数组(有索引和length属性)对象,我们可以使用Array.from得到一个真正的数组,之后便可以调用方便的数组方法。

循环

数组有三种循环方式:

  • for (let i=0; i<arr.length; i++) — 运行得最快,可兼容旧版本浏览器。
  • for (let item of arr) — (迭代器)现代语法,只能访问 items。
  • for (let i in arr) — (对象)永远别用。

length

数组中的length属性其实是其最大的数字索引值加一,如果我们减少它,则数组会发生不可逆截断。

清空数组的最简单方式就是arr.length = 0;

Object.keys(obj)

对于对象来说,我们使用的调用语法是 Object.keys(obj),而不是 obj.keys()。因为对象是所有复杂结构的基础。

Object.* 方法返回的是“真正的”数组对象,而不只是一个可迭代对象。(虽然抽象程度变低了,但是是历史原因,就这样吧)

1
2
3
4
let user = {
name: "John",
age: 30
};
  • Object.keys(user) = ["name", "age"]
  • Object.values(user) = ["John", 30]
  • Object.entries(user) = [ ["name","John"], ["age",30] ]

解构赋值

  • 解构赋值可以简洁地将一个对象或数组拆开赋值到多个变量上。

  • 解构对象的完整语法:

    1
    let {prop : varName = default, ...rest} = object

    这表示属性 prop 会被赋值给变量 varName,如果没有这个属性的话,就会使用默认值 default

    没有对应映射的对象属性会被复制到 rest 对象。

  • 解构数组的完整语法:

    1
    let [item1 = default, item2, ...rest] = array

    数组的第一个元素被赋值给 item1,第二个元素被赋值给 item2,剩下的所有元素被复制到另一个数组 rest

  • 从嵌套数组/对象中提取数据也是可以的,此时等号左侧必须和等号右侧有相同的结构。

  • 对“数组”的解构赋值可以范围拓展到可迭代对象上,如Map,Set等。

  • [guest, admin] = [admin, guest];// js用一行交换变量值