数组是 JavaScript 中最核心的数据结构之一,几乎所有业务场景都会用到。但你真的掌握了数组的 "创建" 和 "遍历" 吗?
一、创建数组的 5 种方法:选对起点很重要
创建数组的方式看似简单,但不同方法的行为差异可能让你踩坑。
[]数组字面量
这是最常用和直接的方式,使用方括号 []
来创建一个数组。
go
```javascript
let arr = [1, 2, 3];
```
特点:
- 简洁直观:是创建数组最常用的方式,几乎所有场景都能覆盖;
- 支持稀疏数组 :允许
[1, , 3]
这样的写法,中间的空槽会被视为empty
(但遍历可能出问题,不推荐); - 直接赋值:创建时可以直接指定元素,无需额外处理。
适用场景:
- 已知初始元素的数组(如
[1, 2, 3]
); - 临时创建空数组(如
const arr = [];
后续动态添加元素)。
new Array()
------ 参数不同,行为大不同
new Array()
的行为很 "特殊",它的效果取决于传入的参数数量:
(1)传入 1 个数字参数:创建指定长度的空数组
js
const arr=new Array(5);//指定了大小
console.log(arr);

这种数组的 "空槽"(empty
)和undefined
不同:
- 空槽不会被
forEach
、map
等方法遍历(跳过); - 用
for...in
遍历会忽略空槽(但for...of
会遍历为undefined
)。
js
const arr2= new Array(5).fill(undefined)
arr2[8]=undefined;
console.log(arr2);

js
for(let key in arr2){
console.log(key,arr2[key]);
}

(2)传入多个参数:创建包含元素的数组
javascript
const arr = new Array(1, 2, 3);
console.log(arr); // [1, 2, 3](和字面量[1,2,3]效果一致)
(3)踩坑示例:想创建长度为 5、元素为undefined
的数组?
javascript
// 错误写法:new Array(5)创建的是empty,不是undefined
const arr = new Array(5);
console.log(arr[0] === undefined); // true(访问时返回undefined)
console.log(arr.includes(undefined)); // false(因为实际是empty)
// 正确写法:用fill()填充undefined
const arr2 = new Array(5).fill(undefined);
console.log(arr2.includes(undefined)); // true(真正的undefined)
适用场景:
- 明确需要 "指定长度的空数组"(如后续用
fill
填充固定值); - 不推荐用于创建已知元素的数组(不如字面量直观)。
Array.of()
------ 参数统一视为元素
Array.of()
是 ES6 新增的静态方法,专门解决new Array()
的参数歧义问题:无论传入多少个参数,都被视为数组元素。
javascript
// 1. 传入1个数字:视为元素,不是长度
const arr1 = Array.of(5);
console.log(arr1); // [5](长度为1,元素是5)
// 2. 传入多个参数:和字面量效果一致
const arr2 = Array.of(1, 2, 3);
console.log(arr2); // [1, 2, 3]
// 3. 传入非数字参数:正常处理
const arr3 = Array.of('a', true, null);
console.log(arr3); // ['a', true, null]
和new Array()
的对比:
方法 | Array.of(5) |
new Array(5) |
---|---|---|
结果 | [5] (元素为 5) |
[empty × 5] (长度 5) |
参数处理 | 所有参数都视为元素 | 单个数字参数视为长度 |
适用场景:
- 动态创建数组(如参数数量不确定时,确保参数被当作元素);
- 替代
new Array()
,避免因参数数量导致的歧义。
Array.from()
------ 灵活转换的利器
Array.from()
是 ES6 新增的静态方法,用于将类数组对象(如arguments
、DOM 集合)或可迭代对象(如Set
、Map
)转换为真正的数组 。它还支持第二个参数(类似map
的回调),可以在转换时处理元素。
(1)基本用法:转换类数组
javascript
// 类数组对象(有length和索引,但不是数组)
const likeArray = { 0: 'a', 1: 'b', length: 2 };
const arr = Array.from(likeArray);
console.log(arr); // ['a', 'b'](转为真正的数组)
(2)进阶用法:配合回调处理元素
javascript
// 创建26个大写字母数组(A-Z)
const letters = Array.from(
new Array(26), // 长度为26的空数组
(_, index) => String.fromCharCode(65 + index) // 65是'A'的ASCII码
);
console.log(letters); // ['A', 'B', ..., 'Z']
(3)转换可迭代对象(如 Set)
javascript
const set = new Set([1, 2, 3, 3]); // Set自动去重
const arr = Array.from(set);
console.log(arr); // [1, 2, 3]
特点:
- 处理空槽 :
Array.from(new Array(5))
会将空槽转为undefined
; - 支持映射 :第二个参数可以对每个元素做处理,相当于
map
的简化版; - 真正的数组:返回的是标准数组,支持所有数组方法。
适用场景:
- 转换类数组对象(如 DOM 集合
document.querySelectorAll('div')
); - 生成有规律的数组(如字母表、数字序列);
- 转换可迭代对象(如
Set
、Map
)为数组。
扩展运算符...
:复制或合并数组
严格来说,扩展运算符是 "数组复制 / 合并" 的工具,但也可用于创建新数组:
javascript
// ① 复制数组
const arr = [1, 2, 3];
const copy = [...arr];
// ② 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
// ③ 转换可迭代对象(同Array.from())
const set = new Set([1, 2, 3]);
const arrFromSet = [...set]; // [1, 2, 3]
扩展:稀疏数组 vs 密集数组
创建数组时,一定要注意 "稀疏数组"(含空槽)和 "密集数组"(含实际元素)的区别:
类型 | 示例 | forEach 遍历 |
includes(undefined) |
推荐度 |
---|---|---|---|---|
稀疏数组 | [1, , 3] 、new Array(3) |
跳过空槽 | false | ❌ 不推荐 |
密集数组 | [1, undefined, 3] 、new Array(3).fill(undefined) |
遍历所有元素 | true | ✅ 推荐 |
记住一个原则:优先用数组字面量[]
,复杂场景用Array.from()
,避免手动创建稀疏数组。
二、遍历数组的 6 种方法:避开这些坑!
创建数组后,遍历是最常用的操作。不同遍历方式的特性差异很大,选错了可能导致逻辑错误。
1. 基础 for 循环(计数循环):性能王者
javascript
const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
console.log(`索引${i}:`, arr[i]);
if (arr[i] === 2) break; // 可中断循环
}
特点:
- 性能最优(直接操作索引,无额外开销);
- 可通过
break
/continue
控制循环; - 需手动维护索引(稍显繁琐)。
适用场景:对性能要求高、需要中断循环的场景。
2. forEach
:简洁但无法中断
javascript
const arr = [1, 2, 3, 4];
arr.forEach((item, index) => {
console.log(`索引${index}:`, item);
if (item === 2) return; // 仅跳过当前次,无法中断整个循环
});
坑点:
- 不能中断 :
return
、break
均无效(break
会报错); - 跳过空槽 :对稀疏数组
[1, , 3]
,会跳过empty
元素; - 不改变原数组 :但可通过索引修改(
arr[index] = ...
)。
适用场景:简单遍历,且不需要中断循环的场景。

3. for...of
:迭代器遍历,兼顾优雅与灵活
javascript
const arr = [1, 2, 3, 4];
// ① 遍历元素
for (const item of arr) {
console.log(item);
if (item === 2) break; // 可中断
}
// ② 同时获取索引和元素(用entries())
for (const [index, item] of arr.entries()) {
console.log(`索引${index}:`, item);
}
entries()
是数组的内置方法,返回一个迭代器对象 ,迭代结果为[索引, 元素]
数组。
特点:
- 支持中断 :可使用
break
、continue
; - 遍历所有元素 :包括
undefined
,但对空槽(empty
)视为undefined
; - 适用于所有可迭代对象 :数组、
Set
、Map
、字符串等。
适用场景:大多数需要灵活控制(中断、获取索引)的遍历场景。
4. 数组方法:map
/filter
/find
等(遍历 + 处理)
这些方法本质上是 "遍历 + 处理" 的组合,返回新数组或值:
map
:遍历并返回新数组(一一映射);filter
:遍历并返回符合条件的元素组成的新数组;find
:遍历并返回第一个符合条件的元素;some
:判断是否有至少一个元素符合条件(返回布尔值);every
:判断是否所有元素都符合条件(返回布尔值)。
javascript
const arr = [1, 2, 3, 4];
// map:返回新数组
const doubled = arr.map(item => item * 2); // [2, 4, 6, 8]
// filter:筛选元素
const evens = arr.filter(item => item % 2 === 0); // [2, 4]
// find:查找第一个偶数
const firstEven = arr.find(item => item % 2 === 0); // 2
注意:
- 这些方法均不改变原数组;
- 除
some
/every
外,均遍历所有元素(无法中途中断)。
5. reduce
:累加器,处理复杂逻辑
reduce
是最强大的数组方法之一,可将数组 "缩减" 为一个值(或对象、数组等):
javascript
const arr = [1, 2, 3, 4, 5];
// ① 求和
const sum = arr.reduce((prev, curr) => prev + curr, 0); // 15
// ② 求最大值
const max = arr.reduce((prev, curr) => Math.max(prev, curr), -Infinity); // 5
// ③ 分组(复杂处理)
const people = [
{ name: 'Alice', age: 20 },
{ name: 'Bob', age: 20 },
{ name: 'Charlie', age: 30 }
];
const groupedByAge = people.reduce((groups, person) => {
const key = person.age;
if (!groups[key]) groups[key] = [];
groups[key].push(person);
return groups;
}, {});
// { 20: [{name: 'Alice', ...}, {name: 'Bob', ...}], 30: [...] }
特点:
- 灵活性极高,可实现求和、分组、转换等复杂逻辑;
- 遍历所有元素,无法中断。
适用场景:需要对数组元素进行累加、聚合、转换等复杂处理时。
6. for...in
:遍历对象的 "坑王",慎用!
javascript
const arr = [1, 2, 3];
// 给数组添加自定义属性
arr.customProp = '我是自定义属性';
// for...in会遍历所有可枚举属性(包括自定义属性和原型链属性)
for (const key in arr) {
console.log(key); // 0, 1, 2, customProp(意外遍历到自定义属性)
}
坑点:
- 遍历所有可枚举属性:包括数组的自定义属性、原型链上的属性;
- 索引是字符串类型 :
key
是"0"
、"1"
而非数字; - 跳过空槽但遍历自定义属性:行为混乱。
结论 :永远不要用for...in
遍历数组!它是为对象设计的。