JavaScript 中如何采用创建新数组避免副作用

作者:袖梨 2026-07-10
避免副作用最直接有效的方式是创建新数组而非修改原数组,需切断引用关系、不触碰原始数据,优先选用map/filter/concat等返回新数组的方法,慎用push/splice等原地操作。

创建新数组而不是修改原数组,是避免副作用最直接有效的方式。核心在于切断引用关系、不触碰原始数据、让每次操作都可预测可测试。

用非破坏性方法替代原地操作

JavaScript 数组有两类方法:一类直接改原数组(如 pushpopsplicereversesortfill),另一类返回新数组(如 mapfilterconcatsliceflatMap)。只要目标是“生成结果”,就应优先选后者。

  • 要加元素?不用 arr.push(item),改用 [...arr, item]arr.concat(item)
  • 要删元素?不用 arr.splice(i, 1),改用 arr.filter((_, idx) => idx !== i)
  • 要反转?不用 arr.reverse(),改用 [...arr].reverse()arr.slice().reverse()
  • 要排序?不用 arr.sort(),改用 [...arr].sort((a, b) => a - b)

复制时注意深浅层级

浅拷贝只断开第一层引用,适合纯值数组或你确定内部对象不会被意外修改的场景;若数组含对象、日期、嵌套数组等,浅拷贝后仍共用内部引用,此时需深拷贝。

  • 一维数值/字符串数组:用 [...arr]arr.slice()Array.from(arr) 即可
  • 含对象的数组:[...arr] 只复制对象引用,修改 newArr[0].name 仍会影响原对象
  • 需要真正隔离:用 structuredClone(arr)(现代环境)、JSON.parse(JSON.stringify(arr))(简单结构,不含函数/undefined/Symbol)

把数组生成逻辑参数化

硬编码数组(如 const list = [{id: 1}, {id: 2}])会让测试难以覆盖边界情况。把创建过程变成函数,输入决定输出,副作用自然消失。

立即学习“Java免费学习笔记(深入)”;

  • 函数应只依赖参数,不读取 localStorage、不调用 Date.now()、不发起请求
  • 示例:function createItems(count, type = 'default') { return Array.from({ length: count }, (_, i) => ({ id: i + 1, type })) }
  • 测试时可传入 01100 或注入异常字段,完全掌控输入

声明方式不是关键,操作意图才是

const 只防止变量重新赋值,无法阻止对象内部被改。真正起作用的是“是否新建内存空间”。哪怕用 let,只要每次操作都基于副本,就不会污染原始数据。

  • 错误示范:const arr = [1,2,3]; arr.push(4); —— 原数组已变
  • 正确示范:const arr = [1,2,3]; const newArr = [...arr, 4]; —— 原数组完好,新数组独立
  • 组件中接收 props 数组时,不要直接 props.items.sort(),先拷贝再处理

相关文章

精彩推荐