




JavaScript数组中会改变原数组的方法有push、pop、shift、unshift、splice、reverse、sort、fill、copyWithin;安全生成新数组应优先使用展开语法与非变异方法组合。
JavaScript 数组方法很多,但真正高频、安全、可预测的其实就十几个;盲目用 map、filter 并不等于高效,关键看是否匹配场景、是否产生意外副作用、是否误用稀疏数组或 undefined。
这是最容易踩坑的一类——你以为在“转换”,结果把原始数据改了,后续逻辑全乱。
push、pop、shift、unshift:增删首尾元素,返回新长度或被删值,原数组变splice:万能修改器,既能删又能插还能替,原数组变,返回被删元素数组reverse、sort:看起来是“整理”,实则原地翻转或排序,sort() 默认按字符串比较,[10, 2].sort() 得到 [10, 2] 而不是 [2, 10]
fill、copyWithin:冷门但危险,直接写入原数组指定位置⚠️ 注意:forEach 不改变原数组,但它也不返回新数组——别把它当 map 用;想链式调用或生成新数据,优先选非 mutating 方法。
除非明确需要副作用,否则默认应使用不可变操作。现代 JS 中最可靠的是展开语法 + 非 mutating 方法组合。
push:[...arr, newItem](比 arr.concat(item) 更直观)unshift:[newItem, ...arr]
filter + map 连用:arr.filter(x => x > 0).map(x => x * 2) 安全且可读JSON.parse(JSON.stringify(arr)) 会丢函数、undefined、Date、NaN、循环引用;真要深拷贝考虑 structuredClone(arr)(现代环境支持)或专用库⚠️ 注意:slice() 和 concat() 是浅拷贝,对象元素仍共享引用——arr.slice()[0].prop = 1 会改原始数组里的对象。
find / findIndex vs indexOf / includes 怎么选?核心区别在于匹配逻辑粒度:基础值查等号,复杂条件必须用 find 系列。
indexOf 和 includes 只做严格相等(===),适用于字符串、数字、布尔值等基本类型查找find 接收回调函数,可基于任意条件返回第一个匹配项(如 arr.find(x => x.id === 123))findIndex 返回索引而非值,适合后续要 splice 或更新的场景;但注意它返回 -1 表示未找到,不是 falsy 值,别直接 if (findIndex(...))
some 和 every 是布尔判断替代品,比 find 更轻量(不构造中间值)⚠️ 注意:includes 对 NaN 友好([NaN].includes(NaN) === true),而 indexOf(NaN) 返回 -1,因为 NaN !== NaN。
不是所有 for 循环都该被取代。当数组很大(>10k)、逻辑简单、或需提前退出时,传统循环仍有优势。
for 循环可随时 break 或 continue,而 some/every 虽可中断,filter/map 则必须遍历全部for 避免了函数调用开销和闭包创建,在 V8 等引擎中更容易被优化for...of 比 forEach 略快(无回调函数),且支持 break;但不兼容稀疏数组(会跳过空槽位)arr
.length —— 提前缓存,如 for (let i = 0, len = arr.length; i
真实项目里,90% 场景用 map/filter/find 完全没问题;但如果你在处理实时渲染数据、解析大 JSON、或做 canvas 像素计算,手写 for 往往更可控。