js中数组详解

操作数组的方法

a. 改变原数组 (Mutator Methods) - 要小心使用!
方法 描述 返回值
push() 在数组末尾添加一个或多个元素 新的 length
pop() 删除并返回数组的最后一个元素 被删除的元素
unshift() 在数组开头添加一个或多个元素 新的 length
shift() 删除并返回数组的第一个元素 被删除的元素
splice() 万能方法:删除、插入、替换元素 被删除元素组成的数组
sort() 对数组进行原地排序 排序后的数组
reverse() 原地反转数组元素的顺序 反转后的数组
fill() 用一个固定值填充数组中从起始索引到终止索引内的全部元素 修改后的数组
copyWithin() 浅复制数组的一部分到同一数组中的另一个位置 修改后的数组

重点讲解 splice 和 sort:

  • splice(start, deleteCount, ...items)

    js 复制代码
    const months = ['Jan', 'March', 'April', 'June'];
    // 插入元素
    months.splice(1, 0, 'Feb'); // 在索引1处,删除0个元素,插入'Feb'
    // months -> ['Jan', 'Feb', 'March', 'April', 'June']
    
    // 替换元素
    months.splice(4, 1, 'May'); // 在索引4处,删除1个元素,插入'May'
    // months -> ['Jan', 'Feb', 'March', 'April', 'May']
    
    // 删除元素
    const removed = months.splice(2, 2); // 从索引2开始,删除2个元素
    // months -> ['Jan', 'Feb', 'May']
    // removed -> ['March', 'April']
  • sort(compareFunction)

    • 默认行为 :将元素转换为字符串,然后按 UTF-16 码元值排序。这对于数字排序是灾难性的!

      js 复制代码
      const numbers = [10, 5, 40, 25];
      numbers.sort(); // -> [10, 25, 40, 5] (因为 '10' < '25' < '40' < '5')
    • 正确用法:必须提供一个比较函数。

      js 复制代码
      // 升序
      numbers.sort((a, b) => a - b); // -> [5, 10, 25, 40]
      // 降序
      numbers.sort((a, b) => b - a); // -> [40, 25, 10, 5]
      . 不改变原数组,返回新数组 (Immutable Methods) - 推荐使用!
方法 描述 返回值
slice() 提取数组的一部分,返回一个新数组 包含提取元素的新数组
concat() 合并两个或多个数组 一个新的合并后的数组
join() 将数组所有元素连接成一个字符串 字符串
map() 创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果 一个新的、经过转换的数组
filter() 创建一个新数组, 其包含通过所提供函数实现的测试的所有元素 一个新的、经过过滤的数组
reduce() 对数组中的每个元素执行一个 "reducer" 函数,将其结果汇总为单个返回值 函数累计处理的结果

重点讲解 map, filter, reduce (函数式编程三巨头):

  • map(callback(element, index, array))转换

    codeJavaScript

    ini 复制代码
    const users = [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}];
    const names = users.map(user => user.name); // -> ['Alice', 'Bob']
    const userDivs = users.map(user => `<div>${user.name}</div>`); // 在 React 中非常常用
  • filter(callback(element, index, array))筛选

    codeJavaScript

    ini 复制代码
    const numbers = [1, 2, 3, 4, 5, 6];
    const evens = numbers.filter(num => num % 2 === 0); // -> [2, 4, 6]
  • reduce(callback(accumulator, currentValue, currentIndex, array), initialValue)聚合/汇总

    • accumulator:累加器,上一次回调的返回值。
    • currentValue:当前处理的元素。
    • initialValue:可选,累加器的初始值。强烈建议总是提供初始值!

    codeJavaScript

    ini 复制代码
    // 1. 求和
    const sum = [1, 2, 3, 4].reduce((acc, cur) => acc + cur, 0); // -> 10
    
    // 2. 数组去重
    const uniqueArr = [1, 2, 2, 3, 4, 3].reduce((acc, cur) => {
        if (!acc.includes(cur)) {
            acc.push(cur);
        }
        return acc;
    }, []); // -> [1, 2, 3, 4]
    
    // 3. 将对象数组按属性分组
    const people = [
        { name: 'Alice', age: 21 },
        { name: 'Max', age: 20 },
        { name: 'Jane', age: 20 }
    ];
    const groupedByAge = people.reduce((acc, person) => {
        const age = person.age;
        if (!acc[age]) {
            acc[age] = [];
        }
        acc[age].push(person);
        return acc;
    }, {});
    // -> { '20': [ { name: 'Max', age: 20 }, { name: 'Jane', age: 20 } ], '21': [ { name: 'Alice', age: 21 } ] }
c. 查找与判断方法 (通常不改变原数组)
方法 描述 返回值
indexOf() 返回指定元素在数组中第一次出现的索引,否则返回 -1 索引或 -1
lastIndexOf() 返回指定元素在数组中最后一次出现的索引,否则返回 -1 索引或 -1
includes() 判断一个数组是否包含一个指定的值 (ES6) true 或 false
find() 返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined (ES6) 元素值或 undefined
findIndex() 返回数组中满足提供的测试函数的第一个元素的索引,否则返回 -1 (ES6) 索引或 -1
some() 测试数组中是不是至少有1个元素通过了被提供的函数测试 true 或 false
every() 测试一个数组内的所有元素是否都能通过某个指定函数的测试 true 或 false

includes vs indexOf:includes 更直观,且能正确处理 NaN,而 indexOf 不能。

codeJavaScript

scss 复制代码
[1, 2, NaN].indexOf(NaN); // -> -1 (错误)
[1, 2, NaN].includes(NaN); // -> true (正确)

4. ES6+ 新特性

  • 展开语法 (Spread Syntax ...) :非常强大,用于浅拷贝、合并数组、函数传参等。

    codeJavaScript

    ini 复制代码
    const arr1 = [1, 2, 3];
    const arr2 = [4, 5, 6];
    
    // 浅拷贝
    const copiedArr = [...arr1];
    
    // 合并数组
    const mergedArr = [...arr1, ...arr2]; // -> [1, 2, 3, 4, 5, 6]
    
    // 字符串转数组
    const chars = [...'hello']; // -> ['h', 'e', 'l', 'l', 'o']
  • 解构赋值 (Destructuring Assignment) :快速从数组中提取值。

    codeJavaScript

    ini 复制代码
    const [first, second, , fourth] = [10, 20, 30, 40];
    // first -> 10, second -> 20, fourth -> 40
    
    // 配合剩余参数
    const [head, ...tail] = [1, 2, 3, 4, 5];
    // head -> 1, tail -> [2, 3, 4, 5]

5. 性能考量与最佳实践

  1. Immutability 优先:在 React/Vue/Angular 等框架中,状态更新依赖于不可变性。优先使用 map, filter, reduce, slice, ...展开语法, 以及最新的 toSorted 等方法,而不是 push, splice, sort。这能避免很多难以追踪的 bug。

  2. 警惕 unshift 和 shift:这两个方法在数组头部操作,会导致所有后续元素的索引重新计算,时间复杂度是 O(n)。对于大数据量的数组,性能开销很大。而 push 和 pop 在尾部操作,是 O(1),非常快。如果需要频繁在头部操作,可以考虑使用双端队列(Deque)等更合适的数据结构。

  3. 选择合适的遍历方式

    • 需要性能极致且可以 break/continue:for 循环。
    • 简洁且只需遍历,不关心返回值:forEach。
    • 需要转换/筛选并返回新数组:map / filter。
    • 需要遍历可迭代对象(不只是数组):for...of。
    • 避免使用 for...in 遍历数组,它会遍历数组原型链上的属性,且顺序不保证。
  4. Array.from vs ... :两者都可以将类数组对象转为数组。Array.from 更强大,因为它有第二个 map 功能的参数,可以在转换的同时处理元素,一步到位。


6. 数组的遍历方式总结

方式 优点 缺点
for 循环 性能最高,可使用 break, continue 写法相对繁琐
forEach 语法简洁,语义清晰 无法中途跳出循环(break 无效)
for...of 语法简洁,可遍历所有可迭代对象,可使用 break, continue 无法直接获取索引(需配合 entries())
map/filter 函数式,链式调用,返回新数组,代码清晰 专用于转换/筛选,不应仅为遍历而用
for...in 不要用于遍历数组 遍历的是键(key),会遍历原型链,顺序不定
while / do...while 灵活,可控制循环条件 容易写出死循环

数组去重的写法

1、用set

js 复制代码
let arr = [1,2,3,4,5,6,0,10,0]

console.log([...new Set(arr)]);

2、用reduce

js 复制代码
let arr = [1,2,3,4,5,6,0,10,0]

let arr2 = arr.reduce((acc, cur) => {
if(!acc.includes(cur)){
 acc.push(cur)
}
return acc
},[])

console.log(arr2);
相关推荐
崽崽长肉肉4 小时前
iOS 基于Vision.framework从图片中提取文字
前端
温宇飞4 小时前
Web Abort API - AbortSignal 与 AbortController
前端
Tomoon4 小时前
前端开发者的全栈逆袭
前端
聪明的笨猪猪4 小时前
Java JVM “垃圾回收(GC)”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
Filotimo_5 小时前
2.CSS3.(3).html
前端·css3
whyfail5 小时前
React v19.2版本
前端·javascript·react.js
慧慧吖@5 小时前
react基础
前端·javascript·react.js
浪裡遊5 小时前
MUI组件库与主题系统全面指南
开发语言·前端·javascript·vue.js·react.js·前端框架·node.js