JavaScript数组是键为字符串数字的特殊对象,非连续内存块;for...in会遍历所有可枚举属性(含继承和自定义键)且不保序,故不适用于数组遍历。

JavaScript 中的数组不是传统意义上的“连续内存块”,而是一个特殊的对象,键名是字符串化的数字索引(如 "0"、"1"),同时自带 length 属性和大量原型方法。理解这点,才能避开遍历时漏项、误判类型、修改原数组等常见问题。
为什么 for...in 不该用来遍历数组
for...in 遍历的是对象的所有可枚举属性,包括继承来的、手动添加的非数字键,且不保证顺序。对数组使用它,可能遍历到 myArr.customProp 或 Array.prototype.map(如果原型被污染)。
- 数组的
length是自有属性,但for...in仍可能跳过稀疏位置(如arr[5] = 'x'但arr[0]未定义) - 若数组被扩展过(如
arr.foo = 'bar'),这个foo会被遍历到 - 推荐用
for (let i = 0; i 或for...of
forEach 和 for...of 的关键区别
forEach 是数组方法,只适用于数组(或类数组对象需 Array.from 转换);for...of 是语言级语法,可遍历任何实现了 Symbol.iterator 的结构(如 Set、Map、字符串)。
-
forEach无法用break或continue中断循环,想提前退出得抛错或改用some/every -
for...of支持break、continue,也支持解构:for (const [index, value] of arr.entries()) -
forEach回调的第三个参数是数组本身,常被忽略但可用于避免闭包引用外部变量
哪些操作会改变原数组,哪些不会
区分“变异方法”和“非变异方法”是避免意外交互的关键。比如在 React 的 useState 中直接 push 会导致渲染失效。
立即学习“Java免费学习笔记(深入)”;
- 会改变原数组:
push、pop、shift、unshift、splice、sort、reverse、fill、copyWithin - 不会改变原数组:
map、filter、slice、concat、flat、toSorted(ES2024)、toReversed -
splice返回被删除的元素数组,不是原数组;slice返回新数组,原数组不变
const arr = [1, 2, 3]; arr.push(4); // arr 变成 [1, 2, 3, 4] —— 原地修改 const newArr = arr.map(x => x * 2); // arr 仍是 [1, 2, 3, 4],newArr 是 [2, 4, 6, 8]
稀疏数组与 undefined 的陷阱
数组可以是稀疏的(arr[10] = 'x' 时,索引 0–9 并不存在,不是 undefined),这会影响 map、forEach、for...of 的行为——它们会跳过这些空位。
-
arr[0] = undefined是“有值为undefined”,arr[1]未赋值是“空位”(in操作符返回false) -
JSON.stringify([, undefined])得到[null],因为undefined被转成null,空位被忽略 - 检测是否为真正空位:用
arr.hasOwnProperty(index)或index in arr
数组的“类对象”本质决定了很多看似直觉的行为其实有底层依据。写代码时多问一句“这个操作是在读属性,还是在调用方法,还是在触发原型链”,比死记 API 更可靠。











