JavaScript数组
JavaScript / ECMAScript Array 的定位、创建方式、常用方法与高频边界。
#type / concept
#status / growing
#resource / javascript
#resource / ecmascript
[!info] related notes
JavaScript数组
Array 是 ECMAScript 里最常用的集合类型之一。它本质上是特殊对象,但在使用上更应该把它理解为”有顺序、可变长、带大量内置方法的数据容器”。
最短理解
- 有顺序,可以通过索引访问
- 动态长度,可以随时增删元素
- 同一个数组里可以混合多种值类型
- 它是对象,但语义更偏”列表容器”
const arr = [1, 'hello', { name: 'Alice' }, [2, 3]];
创建方式
- 数组字面量:
[] Array.of(...)Array.from(...)new Array(...)
默认更推荐字面量;new Array(5) 这种单个数字参数写法容易让人误读成”有一个元素的数组”,实际上它创建的是长度为 5 的稀疏数组。
空位与稀疏数组
const options = [,,,,,];
这表示存在 5 个空位,不等于 5 个显式的 undefined。很多数组方法在处理空位时的行为并不完全一致,所以除非确实需要,通常不建议主动制造稀疏数组。
如何判断一个值是不是数组
优先使用 Array.isArray()。
instanceof Array 在跨 iframe / 跨 realm 场景里可能失准,而 Array.isArray() 更稳定。
完整方法速查表
增删改(修改原数组)
| 方法 | 作用 | 返回值 | 备注 |
|---|---|---|---|
.push(...items) | 末尾添加 | 新长度 | |
.pop() | 末尾删除 | 被删元素 | |
.unshift(...items) | 开头添加 | 新长度 | |
.shift() | 开头删除 | 被删元素 | |
.splice(start, deleteCount, ...items) | 任意位置增删改 | 被删元素数组 | 会改原数组 |
.copyWithin(target, start, end?) | 内部复制 | 修改后的数组 | ES6 |
.fill(value, start?, end?) | 填充 | 修改后的数组 | ES6 |
复制与变换(不改原数组)
| 方法 | 作用 | 返回值 |
|---|---|---|
.slice(start?, end?) | 浅拷贝片段 | 新数组 |
.concat(...items) | 合并数组 | 新数组 |
.flat(depth?) | 扁平化 | 新数组(ES2019) |
.flatMap(callback) | map + flat(1) | 新数组(ES2019) |
.map(callback) | 映射每个元素 | 新数组 |
.filter(callback) | 过滤 | 新数组 |
.reduce(callback, init?) | 累积 | 最终值 |
.reduceRight(callback, init?) | 从右累积 | 最终值 |
查找与判断
| 方法 | 作用 | 返回值 |
|---|---|---|
.indexOf(item, from?) | 查找索引 | 索引或 -1 |
.lastIndexOf(item, from?) | 从右查找索引 | 索引或 -1 |
.includes(item, from?) | 是否包含 | boolean |
.find(callback) | 查找第一个匹配元素 | 元素或 undefined |
.findIndex(callback) | 查找第一个匹配索引 | 索引或 -1 |
.findLast(callback) | 从右查找第一个匹配 | 元素(ES2023) |
.findLastIndex(callback) | 从右查找第一个匹配索引 | 索引(ES2023) |
.some(callback) | 是否有满足条件的 | boolean |
.every(callback) | 是否全部满足条件 | boolean |
排序与反转
| 方法 | 作用 | 返回值 | 备注 |
|---|---|---|---|
.sort(compareFn?) | 排序 | 修改后的数组 | 会改原数组 |
.reverse() | 反转 | 修改后的数组 | 会改原数组 |
.toSorted(compareFn?) | 排序(不改原数组) | 新数组(ES2023) | |
.toReversed() | 反转(不改原数组) | 新数组(ES2023) |
遍历
| 方法 | 作用 | 返回值 |
|---|---|---|
.forEach(callback) | 遍历 | undefined |
.map(callback) | 映射 | 新数组 |
.filter(callback) | 过滤 | 新数组 |
.reduce(callback, init?) | 累积 | 最终值 |
.some(callback) | 部分满足? | boolean |
.every(callback) | 全部满足? | boolean |
.keys() | 键迭代器 | Iterator |
.values() | 值迭代器 | Iterator |
.entries() | 键值对迭代器 | Iterator |
转换与拼接
| 方法 | 作用 | 返回值 |
|---|---|---|
.join(separator?) | 转字符串 | string |
.toString() | 转字符串(逗号分隔) | string |
.toLocaleString() | 本地化字符串 | string |
.concat(...items) | 拼接数组 | 新数组 |
...spread | 展开运算符 | (语法) |
静态方法
| 方法 | 作用 |
|---|---|
Array.isArray(v) | 判断是否数组 |
Array.from(iterable, mapFn?) | 从可迭代对象创建数组 |
Array.of(...items) | 从参数创建数组 |
Array.fromAsync(iterable, mapFn?) | 异步版本(ES2024) |
最容易踩坑的点
sort 默认按字符串比较
[10, 9, 80, 1].sort(); // [1, 10, 80, 9](按字符串排序)
[10, 9, 80, 1].sort((a, b) => a - b); // [1, 9, 10, 80](数值排序)
slice vs splice
// slice 不改原数组,返回新数组
const arr = [1, 2, 3, 4, 5];
arr.slice(1, 3); // [2, 3]
arr; // [1, 2, 3, 4, 5]
// splice 改原数组,返回被删除的元素
arr.splice(1, 2); // [2, 3]
arr; // [1, 4, 5]
map vs forEach
// map 返回新数组
const doubled = [1, 2, 3].map(x => x * 2); // [2, 4, 6]
// forEach 返回 undefined,用于副作用
[1, 2, 3].forEach(x => console.log(x)); // undefined
稀疏数组 vs undefined
const sparse = [1, , 3]; // 稀疏,长度 3,有空位
const explicit = [1, undefined, 3]; // 明确有 undefined
sparse.map(x => x * 2); // [2, empty, 6](跳过空位)
explicit.map(x => x * 2); // [2, NaN, 6]
常用模式
数组去重
const unique = [...new Set(arr)];
const unique2 = arr.filter((v, i) => arr.indexOf(v) === i);
数组扁平化
const nested = [1, [2, [3, [4]]]];
nested.flat(); // [1, 2, [3, [4]]]
nested.flat(2); // [1, 2, 3, [4]]
nested.flat(Infinity); // [1, 2, 3, 4]
分组统计
// 按某个属性分组
const grouped = arr.reduce((acc, item) => {
const key = item.category;
acc[key] = acc[key] || [];
acc[key].push(item);
return acc;
}, {});
// ES2024: Object.groupBy
const grouped2 = Object.groupBy(arr, item => item.category);
数组转 Map
const map = new Map(arr.map(item => [item.id, item]));
排序去重
const sorted = [...new Set(arr)].sort((a, b) => a - b);
分块
function chunk(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
一个常见 reduce() 例子
const arr = [1, 2, [3, 4], [5, 6]];
function flatArray(arr) {
return arr.reduce((result, item) => {
if (Array.isArray(item)) {
return [...result, ...flatArray(item)];
}
return [...result, item];
}, []);
}
这个例子适合用来建立 reduce() 的直觉:它不是”数组专属魔法”,而是把一组元素持续累加到一个结果值里的通用接口。
ES2023 新增方法(不改原数组)
| 方法 | 作用 | 对应的原方法 |
|---|---|---|
.toSorted() | 排序 | .sort() |
.toReversed() | 反转 | .reverse() |
.toSpliced() | splice | .splice() |
.with(index, value) | 替换指定位置 | arr[index] = value |
.findLast() | 从右查找 | .find() |
.findLastIndex() | 从右查找索引 | .findIndex() |
延伸阅读
- 看集合类型总览:ecmascript-collection-reference-types
- 看可迭代协议:Iterable、Iterator、Generator 关系
- 看数组如何被
for...of、展开运算符消费:ecmascript-iterators-and-generators - 看字符串拆分后何时转成数组更合理:ways-to-split-unicode-in-javascript