一、数组的本质:不仅仅是 "有序集合"
1.1 数组的核心特性
- 可遍历的对象 :数组是内置了
Symbol.iterator
的对象,支持for...of
、forEach
等遍历方式,比普通对象多了 "按顺序访问元素" 的天然优势。 - 动态与灵活 :不像 C++/Java 的固定类型数组,JS 数组能存储任意类型数据(数字、字符串、对象甚至数组套数组),且长度自动扩容 ------ 往
arr[100]
赋值会直接让数组长度变为 101,浏览器背后的 V8 引擎会悄悄帮你优化存储(别担心,不是真的开 100 个坑)。
1.2 创建数组的三种姿势
-
数组字面量
[]
:最常用的方式,简洁到飞起!javascriptconst names = ['小莉', '小继', '小盛']; // 直接初始化元素 const emptyArr = []; // 空数组,比喝奶茶还清爽
-
构造函数
new Array()
:-
单参数
new Array(n)
:创建一个长度为n
的 "空数组"(注意!不是填满undefined
,而是包含n
个空槽,用for...of
遍历时会跳过它们)。javascriptconst arr = new Array(5); // [empty × 5],console.log时像在玩"大家来找茬"
-
多参数
new Array(1, 2, 3)
:等价于[1, 2, 3]
,直接按参数创建数组。
-
-
静态方法
Array.of()
:专治new Array(n)
的 "单参数歧义"!不管传几个参数,都老老实实把它们作为数组元素。javascriptArray.of(5); // [5],不再是长度为5的空数组 Array.of(1, 2, 3); // [1, 2, 3],和字面量一样直白
二、深度解析:从 "空数组" 到 "全能选手"
2.1 空数组的秘密:empty
vs undefined
-
new Array(5)
的真相 :V8 引擎为了效率,会创建一个带长度的 "稀疏数组",元素位置存在但值未初始化,用for in
遍历时会跳过(因为没有实际键值),而for...of
/forEach
会视为undefined
。javascriptconst sparseArr = new Array(5); console.log(sparseArr[0]); // undefined(但其实是个空槽) sparseArr.fill(undefined); // 手动填满undefined,让空槽显形
-
正确初始化 :如果需要一个 "有 5 个
undefined
的数组",记得用fill
:javascriptconst validArr = new Array(5).fill(undefined); // 再也不怕遍历漏掉啦
2.2 数组的 "双重身份":索引与哈希
-
索引访问:按数字下标快速定位(O (1) 时间复杂度),和传统数组一样丝滑。
-
哈希特性 :本质是对象,能添加字符串键(但会影响
length
属性)。javascriptconst arr = [1, 2, 3]; arr['name'] = '数组'; // 可以加属性,但遍历数组方法(如forEach)不会处理它
三、遍历与操作:效率与可读性的平衡
3.1 遍历方法大比拼
-
for
循环:性能王者,但写起来像给电脑看的(人脑表示有点累)。javascriptfor (let i = 0; i < arr.length; i++) { /* 干活 */ }
-
forEach
:可读性强,缺点是不能中途break
(遇到 "找到就停" 的需求时,只能靠try/catch
玩花样,有点憋屈)。javascriptnames.forEach(name => { if (name === 'Charlie') return; // 只能"提前返回",不能真正停止 console.log(name); });
-
for...of
:结合了两者优点,能拿到值(还能通过entries()
拿索引),现代 JS 首选!javascriptfor (const [index, value] of arr.entries()) { console.log(`第${index + 1}个元素:${value}`); }
3.2 数组的 "魔法方法":reduce 的终极奥义
-
消灭复杂逻辑 :不管是累加、分组还是对象合并,
reduce
都能把一堆操作浓缩成一个函数,让代码像瑞士军刀一样精简。javascript// 累加数组元素,初始值0不能少(不然第一个元素会被当作初始值哦) const sum = [1, 2, 3, 4, 5].reduce((pre, cur) => pre + cur, 0); // 15 // 分组:把名字按长度分组 const grouped = names.reduce((acc, name) => { const len = name.length; acc[len] = acc[len] || []; acc[len].push(name); return acc; }, {});
四、静态方法:Array.from 的 "变形记"
4.1 从 "类数组" 到真正数组
-
转换类数组对象 (如函数的
arguments
、DOM 节点列表):javascriptfunction logArgs() { const argsArray = Array.from(arguments); // 把伪数组变成真数组 argsArray.forEach(arg => console.log(arg)); }
-
处理可迭代对象(字符串、Map、Set):
javascriptArray.from('abc'); // ['a', 'b', 'c'] const set = new Set([1, 2, 2, 3]); Array.from(set); // [1, 2, 3](去重神器!)
4.2 带 "加工厂" 的转换:map 参数
-
在转换时对每个元素加工,比先转换再
map
更高效(少一次遍历,省点性能是点)。javascript// 生成A-Z的字母数组 const letters = Array.from({ length: 26 }, (_, index) => String.fromCodePoint(65 + index) ); // ['A', 'B', ..., 'Z']
五、避坑指南:这些 "坑" 你踩过吗?
5.1 稀疏数组的陷阱
-
空槽不等于
undefined
:new Array(5)
的空槽在forEach
中会被跳过,而fill(undefined)
后的数组会正常遍历。javascriptconst emptyArr = new Array(3); const definedArr = emptyArr.fill(undefined); console.log(emptyArr[0]); // undefined(但实际是空槽) console.log(definedArr[0]); // undefined(真实存在的值)
5.2 警惕 "伪数组"
- 类数组对象(如
{0: 'a', 1: 'b', length: 2}
)用for in
会遍历所有键(包括非数字键),而Array.from
能正确转换为数组。
六、总结:数组是 JS 的 "瑞士军刀"
从简单的[]
到强大的reduce
、Array.from
,JS 数组凭借灵活的特性和丰富的 API,成为数据处理的核心工具。记住:
-
创建数组首选
[]
或Array.of
,避免new Array(n)
的空槽陷阱; -
遍历用
for...of
,复杂逻辑交reduce
,类数组转换找Array.from
; -
遇到空数组别慌,
fill(undefined)
让它显形!
下次遇到数组相关的问题,记得这篇 "通关秘籍",让数组乖乖成为你的编程好帮手