数组

2026 年 1 月 26 日 星期一(已编辑)
/ ,
5

数组

基于 ECMAScript 2025 规范 Section 23.1

什么是 Array?

Array(数组) 是 JavaScript 中用于存储有序列表的数据结构。

// 创建一个数组
const fruits = ["apple", "banana", "orange"];

// 访问元素(从 0 开始)
fruits[0]; // "apple"
fruits[1]; // "banana"
fruits[2]; // "orange"

// 获取长度
fruits.length; // 3

数组的本质

数组本质上是特殊的对象,但有以下区别:

特性普通对象数组
任意字符串整数索引(0, 1, 2...)
长度无自动管理自动维护 length
迭代不保证顺序按插入顺序
// 数组其实是对象
const arr = [1, 2, 3];
typeof arr; // "object"

// 但如果你用非整数作为 key,它就变成"普通对象属性"了
arr[5.5] = "hello";
arr.length; // 3 —— 长度不变!
arr; // [1, 2, 3, empty × 2, 5.5: "hello"]

一、数组的创建方式

1.1 数组字面量(最常用)

const arr1 = [1, 2, 3];
const arr2 = []; // 空数组
const arr3 = [1, , 3]; // 有空槽 [1, <empty>, 3]

1.2 构造函数 Array()

// 多个参数 = 元素
new Array(1, 2, 3); // [1, 2, 3]

// 单个数字参数 = 长度(创建稀疏数组)
new Array(5); // [empty × 5] —— 5 个空槽

⚠️ 注意:单参数容易产生歧义,new Array(3) 到底是 3 个元素还是长度 3?

1.3 Array.of() —— 解决歧义

// ES6 新增,无论几个参数都当作元素
Array.of(5);        // [5]
Array.of(1, 2, 3);  // [1, 2, 3]
Array.of();         // []

1.4 Array.from() —— 万能转换

// 从类数组对象转换
Array.from({ 0: "a", 1: "b", length: 2 }); // ["a", "b"]

// 从可迭代对象转换
Array.from("hello"); // ["h", "e", "l", "l", "o"]

// 还能顺便做映射(第二个参数是 map 函数)
Array.from({ length: 3 }, (_, i) => i * i); // [0, 1, 4]

1.5 展开运算符 ...

// 克隆数组
const arr = [1, 2, 3];
[...arr]; // [1, 2, 3]

// 合并数组
const a = [1, 2];
const b = [3, 4];
[...a, ...b]; // [1, 2, 3, 4]

// 去重
[...new Set([1, 2, 2, 3])]; // [1, 2, 3]

1.6 Array.fill() —— 填充数组

// 创建固定值的数组
new Array(4).fill(0); // [0, 0, 0, 0]

// fill 还能指定起始位置
[1, 2, 3, 4].fill("x", 1, 3); // [1, "x", "x", 4]

二、数组的"长度"特性

2.1 length 的自动更新

const arr = [1, 2, 3];
arr.length; // 3

// 添加元素会自动延长
arr[5] = 6;
arr.length; // 6 —— 自动更新!
arr; // [1, 2, 3, empty × 2, 6]

2.2 length 可以手动修改

const arr = [1, 2, 3, 4, 5];

// 缩短数组(删除末尾元素)
arr.length = 3;
arr; // [1, 2, 3]

// 清空数组
arr.length = 0;
arr; // []

// 拉长数组(产生空槽)
arr.length = 5;
arr; // [empty × 5]

2.3 稀疏数组

空槽(empty item)undefined 是不同的!

const arr1 = [1, undefined, 3];  // 有 3 个元素,第二个是 undefined
const arr2 = [1, , 3];            // 稀疏数组,中间是"空槽"

arr1.length; // 3
arr2.length; // 3

arr1[1]; // undefined
arr2[1]; // undefined —— 读取时也是 undefined,但"空"和"有值"不同

空槽的创建方式:

Array(3);              // [empty × 3]
[1, , 3];              // [1, <empty>, 3]
const a = [1, 2];
a[5] = 6;              // [1, 2, <3 empty items>, 6]
a.length = 10;         // [1, 2, <8 empty items>]
delete a[1];           // [<empty>, 2, <8 empty items>]

三、添加 / 删除元素(改变原数组)

3.1 push() —— 末尾添加

const arr = [1, 2, 3];
arr.push(4, 5);     // 返回新长度 5
arr;                // [1, 2, 3, 4, 5]

3.2 pop() —— 末尾删除

const arr = [1, 2, 3];
arr.pop();          // 返回删除的元素 3
arr;                // [1, 2]

3.3 unshift() —— 开头添加

const arr = [1, 2, 3];
arr.unshift(0);     // 返回新长度 4
arr;                // [0, 1, 2, 3]

3.4 shift() —— 开头删除

const arr = [1, 2, 3];
arr.shift();        // 返回删除的元素 1
arr;                // [2, 3]

3.5 splice() —— 万能操作(增删改)

const arr = [1, 2, 3, 4, 5];

// 删除:splice(起始索引, 删除个数)
arr.splice(1, 2);   // 返回删除的 [2, 3]
arr;                 // [1, 4, 5]

// 插入:splice(起始索引, 0, 新元素...)
arr.splice(1, 0, 2, 3);
arr;                 // [1, 2, 3, 4, 5]

// 替换:splice(起始索引, 删除个数, 新元素...)
arr.splice(1, 2, "a", "b");
arr;                 // [1, "a", "b", 4, 5]

// 清空全部:splice(0)
arr.splice(0);       // 返回所有元素,arr 变为 []

四、不改变原数组的方法

4.1 slice() —— 切片

const arr = [1, 2, 3, 4, 5];

// slice(起始, 结束) —— 不包括结束位置
arr.slice(1, 4);    // [2, 3, 4]

// 负数索引
arr.slice(-3, -1);  // [3, 4]

// 省略结束位置 = 到末尾
arr.slice(2);       // [3, 4, 5]

// 复制整个数组
arr.slice();        // [1, 2, 3, 4, 5]

4.2 concat() —— 合并

const a = [1, 2];
const b = [3, 4];

a.concat(b, 5, 6);  // [1, 2, 3, 4, 5, 6]
a;                  // [1, 2] —— 原数组不变

4.3 toReversed() / toSorted() / toSpliced() / with()

ES2023 新增的不改变原数组的版本:

const arr = [3, 1, 2];

arr.toReversed();   // [1, 2, 3] —— 翻转
arr;                // [3, 1, 2] —— 原数组不变

arr.toSorted();     // [1, 2, 3] —— 排序
arr;                // [3, 1, 2] —— 原数组不变

arr.toSpliced(1, 1, "x"); // [3, "x", 2] —— 替换
arr;                       // [3, 1, 2]

arr.with(0, "y");    // ["y", 1, 2] —— 修改指定位置
arr;                  // [3, 1, 2]

五、查找元素

5.1 indexOf() / lastIndexOf()

const arr = [1, 2, 3, 2, 1];

arr.indexOf(2);      // 1 —— 首次出现的位置
arr.lastIndexOf(2); // 3 —— 最后一次出现的位置
arr.indexOf(99);     // -1 —— 没找到

5.2 includes() —— ES2016

const arr = [1, 2, 3];

arr.includes(2);     // true
arr.includes(99);   // false

// 注意:includes 能正确处理 NaN
[NaN].indexOf(NaN);    // -1 (错误!应该找到)
[NaN].includes(NaN);    // true (正确)

5.3 find() / findIndex() / findLast() / findLastIndex()

const users = [
  { id: 1, name: "张三" },
  { id: 2, name: "李四" },
  { id: 3, name: "王五" }
];

// find: 返回找到的元素
users.find(u => u.id === 2);     // { id: 2, name: "李四" }
users.find(u => u.id === 99);    // undefined

// findIndex: 返回找到的索引
users.findIndex(u => u.id === 2); // 1

// findLast: 从右往左找
users.findLast(u => u.id < 3);    // { id: 2 }

// findLastIndex: 从右往左找索引
users.findLastIndex(u => u.id < 3); // 1

六、遍历数组

6.1 for...of

const arr = [1, 2, 3];

for (const item of arr) {
  console.log(item); // 1, 2, 3
}

// 还能拿到索引
for (const [index, item] of arr.entries()) {
  console.log(index, item); // 0 1, 1 2, 2 3
}

6.2 forEach()

const arr = [1, 2, 3];

arr.forEach((item, index, array) => {
  console.log(item, index);
});

// forEach 的特点:
// - 返回 undefined(不是链式调用)
// - 不能 break / continue(会遍历全部)
// - 空槽会被跳过
[1, , 3].forEach(x => console.log(x)); // 1, 3(跳过空槽)

6.3 map() —— 映射

const arr = [1, 2, 3];

// 对每个元素执行函数,返回新数组
arr.map(x => x * 2);   // [2, 4, 6]

// 还能拿到索引
arr.map((x, i) => x + i); // [1, 3, 5]

6.4 filter() —— 过滤

const arr = [1, 2, 3, 4, 5];

// 返回满足条件的元素组成的新数组
arr.filter(x => x > 3);  // [4, 5]
arr.filter(x => x % 2 === 0); // [2, 4]

6.5 every() / some()

const arr = [1, 2, 3, 4];

// every: 所有都满足?
arr.every(x => x > 0);  // true
arr.every(x => x > 2);   // false

// some: 至少有一个满足?
arr.some(x => x > 3);   // true
arr.some(x => x > 10);  // false

6.6 reduce() / reduceRight()

const arr = [1, 2, 3, 4];

// 累加
arr.reduce((sum, x) => sum + x, 0);  // 10

// reduceRight 从右往左
arr.reduceRight((str, x) => str + x, ""); // "4321"

reduce 的参数: javascript arr.reduce((累计值, 当前元素, 当前索引, 原数组) => {}, 初始值);


七、数组排序

7.1 sort() —— 原位排序

const arr = [1, 15, 2, 10];

// 默认按字符串排序(会出错!)
arr.sort(); // [1, 10, 15, 2]

// 正确用法:传入比较函数
arr.sort((a, b) => a - b);  // [1, 2, 10, 15] 升序
arr.sort((a, b) => b - a);  // [15, 10, 2, 1] 降序

比较函数规则:

  • 返回负数 → a 在前
  • 返回 0 → 相等
  • 返回正数 → b 在前

7.2 reverse() —— 翻转

const arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1]

八、转换方法

8.1 join() —— 数组转字符串

const arr = [1, 2, 3];

arr.join();      // "1,2,3"
arr.join("");    // "123"
arr.join("-");   // "1-2-3"

// 稀疏数组的空槽会变成空字符串
[1, , 3].join("-"); // "1--3"

8.2 split() —— 字符串转数组(String 方法)

"hello world".split(" "); // ["hello", "world"]
"hello".split("");        // ["h", "e", "l", "l", "o"]

// 还能限制长度
"hello world".split(" ", 2); // ["hello", "world"]

8.3 toReversed() / toSorted() / toSpliced() / with()

见 4.3 节。


九、flat() / flatMap() —— 扁平化

9.1 flat() —— 展平数组

[1, [2, [3, [4]]]].flat();     // [1, 2, [3, [4]]] —— 默认展平 1 层
[1, [2, [3, [4]]]].flat(2);    // [1, 2, 3, [4]]  —— 展平 2 层
[1, [2, [3, [4]]]].flat(Infinity); // [1, 2, 3, 4] —— 全部展平

9.2 flatMap() —— 先 map 后 flat

const arr = [1, 2, 3];

// 相当于
arr.map(x => [x, x * 2]).flat();
// [1, 2, 2, 4, 3, 6]

// flatMap 更简洁
arr.flatMap(x => [x, x * 2]);
// [1, 2, 2, 4, 3, 6]

十、ES2015+ 迭代器方法

10.1 keys() / values() / entries()

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

[...arr.keys()];   // [0, 1, 2]
[...arr.values()];  // ["a", "b", "c"]
[...arr.entries()]; // [[0, "a"], [1, "b"], [2, "c"]]

// 配合 for...of
for (const [index, value] of arr.entries()) {
  console.log(index, value);
}

十一、Array.prototype[Symbol.iterator]

所有数组迭代器方法(for...of、spread、Array.from)底层都调用这个方法。

const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();

iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
iterator.next(); // { value: undefined, done: true }

十二、数组的空槽 vs undefined

特点空槽 (empty)undefined
in 运算符falsetrue
Object.keys()不包含包含
for...of跳过遍历到
forEach跳过遍历到
map/filter跳过处理
const arr = [1, , 3];
1 in arr;           // false —— 空槽位置没有属性
const arr2 = [1, undefined, 3];
1 in arr2;          // true

Object.keys(arr);   // ["0", "2"]
Object.keys(arr2);  // ["0", "1", "2"]

十三、常见误区

13.1 Array(3) vs Array.of(3)

Array(3);     // [empty × 3] —— 长度为 3 的空数组
Array.of(3);  // [3] —— 只有一个元素 3

13.2 splice vs slice

  • splice → 改变原数组(删除/插入/替换)
  • slice → 不改变原数组(返回副本)

13.3 sort 的坑

// 常见错误
[1, 2, 10].sort(); // [1, 10, 2] —— 按字符串排序!

// 正确
[1, 2, 10].sort((a, b) => a - b); // [1, 2, 10]

13.4 forEach 不能链式调用

// 错误
[1, 2, 3].forEach(x => console.log(x)).map(x => x); // TypeError

// 正确 - 先收集结果再操作
const result = [];
[1, 2, 3].forEach(x => result.push(x));

十四、方法速查表

方法返回值改变原数组说明
push()新长度末尾添加
pop()删除的元素末尾删除
unshift()新长度开头添加
shift()删除的元素开头删除
splice()删除的元素增删改
slice()新数组切片
concat()新数组合并
map()新数组映射
filter()新数组过滤
find()元素/undefined查找
findIndex()索引/-1查找索引
includes()boolean是否包含
indexOf()索引/-1查找位置
every()boolean全满足?
some()boolean有一个满足?
reduce()累计结果归约
sort()数组排序
reverse()数组翻转
join()字符串转字符串
flat()新数组扁平化
toReversed()新数组翻转(不改原)
toSorted()新数组排序(不改原)
toSpliced()新数组增删改(不改原)
with()新数组修改指定位置

参考

  • ECMAScript 2025 Language Specification Section 23.1
  • Array 对象及其方法

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...