Skip to content

13|JavaScript 数组与常用方法

数组是 JavaScript 中最常用的数据结构之一:服务器列表、日志条目、接口返回的数据——这些在代码中都是数组。掌握数组的创建、访问、修改和高阶方法,能大幅减少手写循环的代码量。

一、数组创建与访问

字面量创建

js
const empty = [];
const services = ["nginx", "redis", "mysql"];
const mixed = ["text", 42, true, { a: 1 }];   // 可以混存不同类型(不推荐)

Array 构造函数

js
const arr1 = new Array(3);        // [empty × 3],长度为 3 的空槽数组
const arr2 = new Array(1, 2, 3);  // [1, 2, 3]

// 字面量更简洁,推荐用 []
const arr3 = [1, 2, 3];

访问元素

js
const services = ["nginx", "redis", "mysql"];

console.log(services[0]);     // "nginx",索引从 0 开始
console.log(services[2]);     // "mysql"
console.log(services[5]);     // undefined,越界不报错
console.log(services.length); // 3

// 取最后一个元素
console.log(services[services.length - 1]);   // "mysql"
console.log(services.at(-1));                  // "mysql"(ES2022,负数索引从末尾数)

修改元素

js
services[1] = "postgres";     // 修改索引 1 的元素
services[5] = "elasticsearch"; // 索引 3、4 变为空槽,length 变为 6

直接给大于当前长度的索引赋值,会在中间创建空槽(empty slots),可能导致遍历行为异常。

二、增删元素

尾部操作

js
const arr = ["a", "b"];

arr.push("c");       // 末尾添加,arr 变为 ["a", "b", "c"],返回新长度 3
arr.pop();           // 删除末尾,arr 变为 ["a", "b"],返回被删元素 "c"

头部操作

js
arr.unshift("z");    // 头部添加,arr 变为 ["z", "a", "b"],返回新长度 3
arr.shift();         // 删除头部,arr 变为 ["a", "b"],返回被删元素 "z"

push/pop 操作效率高(O(1)),unshift/shift 需要移动所有现有元素(O(n))。频繁在头部增删时,考虑用其他数据结构。

splice

在任意位置增删改:

js
const arr = ["a", "b", "c", "d"];

// 从索引 1 开始,删除 2 个元素,并插入 "x"、"y"
arr.splice(1, 2, "x", "y");   // 返回 ["b", "c"]
console.log(arr);              // ["a", "x", "y", "d"]
参数含义
第 1 个起始索引
第 2 个删除数量(0 表示不删除)
第 3 个及以后要插入的元素

三、查找与判断

indexOf / includes

js
const arr = ["nginx", "redis", "mysql"];

arr.indexOf("redis");     // 1,找到返回索引,找不到返回 -1
arr.indexOf("postgres");  // -1

arr.includes("redis");    // true
arr.includes("postgres"); // false

这两个方法使用 === 做比较。对于对象数组,判断的是引用而非内容:

js
const servers = [{ name: "web-01" }, { name: "web-02" }];
servers.includes({ name: "web-01" });   // false(不是同一个对象引用)

find / findIndex

按条件查找对象:

js
const server = servers.find((s) => s.name === "web-01");
// { name: "web-01" }

const index = servers.findIndex((s) => s.name === "web-01");
// 0

四、排序与反转

sort

默认按字符串 Unicode 排序:

js
const nums = [10, 2, 30, 1];
nums.sort();
console.log(nums);   // [1, 10, 2, 30] —— "10" 的 Unicode 小于 "2"

数字排序需要传入比较函数:

js
nums.sort((a, b) => a - b);     // 升序 [1, 2, 10, 30]
nums.sort((a, b) => b - a);     // 降序 [30, 10, 2, 1]

比较函数返回值:

  • 负数:a 排在 b 前面
  • 正数:a 排在 b 后面
  • 0:顺序不变

reverse

js
const arr = ["a", "b", "c"];
arr.reverse();
console.log(arr);   // ["c", "b", "a"]

sortreverse 都会修改原数组。需要保留原数组时,先复制:

js
const sorted = [...arr].sort((a, b) => a - b);

五、切片与拼接

slice

返回新数组,不修改原数组:

js
const arr = ["a", "b", "c", "d"];

arr.slice(1, 3);     // ["b", "c"]:从索引 1 到 3(不含)
arr.slice(2);        // ["c", "d"]:从索引 2 到末尾
arr.slice(-2);       // ["c", "d"]:最后两个
arr.slice();          // ["a", "b", "c", "d"]:浅拷贝整个数组

concat

合并数组:

js
const a = ["nginx"];
const b = ["redis", "mysql"];
const c = a.concat(b);   // ["nginx", "redis", "mysql"]

也可以用展开运算符:

js
const c = [...a, ...b];

六、高阶方法详解

map

对每个元素做转换,返回等长的新数组:

js
const ports = [80, 443, 3306];
const labels = ports.map((p) => `端口 ${p}`);
// ["端口 80", "端口 443", "端口 3306"]

map 适合"一一转换"的场景。如果只是想遍历执行副作用,用 forEach;如果想筛选,用 filter

filter

返回满足条件的新数组:

js
const services = [
  { name: "nginx", status: "running" },
  { name: "redis", status: "stopped" },
  { name: "mysql", status: "running" },
];

const running = services.filter((s) => s.status === "running");
// [{ name: "nginx", ... }, { name: "mysql", ... }]

reduce

把数组归约为单个值:

js
const nums = [1, 2, 3, 4];

const sum = nums.reduce((acc, n) => acc + n, 0);
// 10

// 对象分组
const grouped = services.reduce((acc, s) => {
  const key = s.status;
  if (!acc[key]) acc[key] = [];
  acc[key].push(s);
  return acc;
}, {});
// { running: [...], stopped: [...] }

reduce 的第一个参数是回调函数(接收累加器和当前元素),第二个参数是初始值。初始值不能省略,否则数组为空时会报错。

七、扁平化与去重

flat

js
const nested = [1, [2, 3], [4, [5, 6]]];

nested.flat();        // [1, 2, 3, 4, [5, 6]]:默认展平一层
nested.flat(2);       // [1, 2, 3, 4, 5, 6]:展平两层

Set 去重

js
const nums = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(nums)];   // [1, 2, 3]

Set 是值不重复集合。先转成 Set 去重,再展开成数组。

八、常见错误

用 typeof 判断数组

js
// 错误
if (typeof arr === "object") {   // 对象也满足

// 正确
if (Array.isArray(arr)) {

sort 默认行为不符合预期

js
const nums = [10, 2, 1];
nums.sort();
console.log(nums);   // [1, 10, 2],不是 [1, 2, 10]

// 正确
nums.sort((a, b) => a - b);

在遍历中修改原数组

js
// 错误
const arr = [1, 2, 3];
arr.forEach((item, index) => {
  arr.splice(index, 1);   // 索引错位,结果不符合预期
});

// 正确:倒序遍历
for (let i = arr.length - 1; i >= 0; i--) {
  if (arr[i] < 2) {
    arr.splice(i, 1);
  }
}

map 的返回值被忽略

js
// 错误:没有使用 map 的返回值,且修改了原数组
services.map((s) => {
  s.status = "running";   // 副作用
});

// 正确:返回新数组,不修改原数组
const updated = services.map((s) => ({
  ...s,
  status: "running",
}));