JavaScript如何操作数组对象【教程】

JavaScript数组方法分“改原数组”和“不改原数组”两类:push/pop/shift/unshift/splice/sort/reverse会修改原数组;map/filter/slice/concat/flat/toReversed/toSorted及[...arr]、Array.from()返回新数组。对象数组需注意浅拷贝陷阱,深拷贝推荐structuredClone()。

JavaScript 操作数组对象,关键不是记一堆方法名,而是分清「改原数组」和「不改原数组」这两类行为——多数 bug 和意外结果都源于混淆了这两者。

哪些数组方法会直接修改原数组?

这类方法会改变 arr 本身,调用后原数组内容就变了,后续逻辑可能出错而不自知:

  • push()pop()shift()unshift():增删首尾元素
  • splice():任意位置增删改(最危险,也最常用)
  • sort():默认按字符串排序,[10, 2].sort() 得到 [10, 2] 而非 [2, 10]
  • reverse()

    :反转顺序,原地操作

示例:const arr = [1, 2]; arr.push(3); console.log(arr); // [1, 2, 3] —— arr 已被修改。

哪些方法返回新数组,原数组不变?

这类更安全,适合函数式写法或避免副作用:

  • map()filter()slice()concat()flat()toReversed()(ES2025)、toSorted()(ES2025)
  • Array.from() 和展开语法 [...arr] 也是无损复制的常用手段

注意:map()filter() 的回调函数里改 arr[i] 不影响原数组,但若回调中显式修改了外部变量或对象属性,则另当别论。

操作对象数组时最容易踩的坑

数组里存的是对象引用,不是深拷贝。以下代码看似“新建”,实则共享引用:

const users = [{id: 1, name: 'Alice'}];
const copied = users.map(u => u); // ❌ 浅拷贝,copied[0] === users[0]
copied[0].name = 'Bob';
console.log(users[0].name); // 'Bob'

需要深拷贝时,谨慎选择方式:

  • 简单结构可用 JSON.parse(JSON.stringify(arr))(但会丢函数、undefined、Date 等)
  • 推荐 structuredClone()(现代环境支持,保留更多类型)
  • 复杂场景用 lodash.cloneDeep() 或手动递归

性能敏感场景下该选哪个方法?

小数组(

  • for 循环比 forEach() 快,且可 breakfor...offor...in 安全(后者遍历属性名)
  • includes()indexOf() !== -1 语义清晰,但底层一样;查找对象建议用 find()some(),别用 includes()(对象比较永远是 false
  • splice() 删除大量元素时比多次 filter() 内存友好,但会改原数组——权衡点在此

真实项目里,先写清楚逻辑,再根据 profiling 数据优化,别过早假设。

真正难的不是记住 mapsplice 的参数顺序,而是每次调用前下意识问一句:这个操作,会让别的地方读到的数组突然变样吗?