程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • HTML

  • CSS

  • JavaScript

    • 简介和用法
    • 变量和数据类型
    • 运算符
    • 数据类型转换
    • 选择结构
    • 循环结构
    • 数组
    • 数组遍历
      • 1. for 循环 vs forEach 方法对比
        • 1.1 基本语法对比
        • 1.2 两种方法的主要区别
        • 1.3 性能比较实例
        • 1.4 循环控制能力对比
        • 1.5 实际应用场景选择
      • 2. map() 方法 —— 高效生成新数组
        • 2.1 基本语法与工作原理
        • 2.2 简单数值转换示例
        • 2.3 字符串处理示例
        • 2.4 对象数组处理(实际开发中最常见)
        • 2.5 使用索引参数
        • 2.6 map()方法的实际应用场景
        • 2.7 与其他数组方法的组合使用
      • 3. filter() 方法 —— 过滤数组中的元素
        • 3.1 基本语法与工作原理
        • 3.2 简单数值过滤示例
        • 3.3 多条件过滤示例
        • 3.4 对象数组过滤(实际开发中最常用)
        • 3.5 使用索引和原数组参数
        • 3.6 过滤空值和无效数据
        • 3.7 在实际项目中的应用场景
        • 3.8 与其他数组方法的组合使用
      • 4. reduce() 方法 —— 数组求和与聚合
        • 4.1 基本语法与工作原理
        • 4.2 理解reduce的工作流程
        • 4.3 简单数值求和示例(详细解析)
        • 4.4 初始值的重要性
        • 4.5 对象数组的属性累加(实际开发常用)
        • 4.6 数组扁平化(多维数组转一维)
        • 4.7 分组统计(高级应用)
        • 4.8 链式操作与管道处理
        • 4.9 实现其他数组方法
        • 4.10 注意事项与最佳实践
      • 5. every() 方法 —— 全部满足条件时返回 true
        • 5.1 基本语法与工作原理
        • 5.2 工作流程图解
        • 5.3 简单示例:检查数组是否全为正数
        • 5.4 对象数组的条件检查(实际应用)
        • 5.5 复杂条件组合
        • 5.6 与其他数组方法的比较
        • 5.7 实际应用场景
        • 5.8 注意事项
      • 6. some() 方法 —— 至少一个元素满足条件时返回 true
        • 6.1 基本语法与工作原理
        • 6.2 工作流程图解
        • 6.3 简单示例:检查数组中是否有偶数
        • 6.4 对象数组的条件检查(实际应用)
        • 6.5 复杂条件组合
        • 6.6 与其他数组方法的比较
        • 6.7 实际应用场景
        • 6.8 注意事项
      • 7. includes() 方法 —— 检查数组是否包含特定值
        • 7.1 基本语法与工作原理
        • 7.2 简单示例:检查数组中是否包含某个数值
        • 7.3 使用第二个参数:指定查找的起始位置
        • 7.4 与字符串比较
        • 7.5 与对象和数组比较
        • 7.6 与其他查找方法的比较
        • 7.7 实际应用场景
        • 7.8 注意事项与兼容性
      • 8. 流式操作与终结方法
        • 8.1 链式调用与流式处理
        • 8.2 终结操作方法
        • 8.2.1 reduce() 和 reduceRight()
        • 8.2.2 join()
        • 8.2.3 find() 和 findIndex()
        • 8.2.4 聚合方法:some()、every()、includes()
        • 8.2.5 toArray() 方法(ES2023)
        • 8.3 实际应用:数据处理管道
        • 8.4 性能考虑与惰性求值
        • 8.5 收集到其他数据结构
        • 8.5.1 收集到对象(Object)
        • 8.5.2 收集到Map
        • 8.5.3 收集到Set
        • 8.5.4 分组收集(Group By)
        • 8.5.5 收集到字符串
        • 8.5.6 实际应用:数据转换管道
    • 函数
    • Debug调试
    • DOM
    • 事件处理
    • BOM(浏览器对象模型)
    • 自定义对象
    • 原型 (Prototype)
    • 内置对象
    • 客户端存储
    • 模块加载方案
  • 前端三剑客
  • JavaScript
scholar
2024-10-21
目录

数组遍历

# 数组遍历

在前端开发中,数组操作非常频繁,选择合适的循环遍历方法不仅能提升开发效率,还能优化代码的可读性和维护性。本文将详细介绍前端常用的数组遍历方法,包括 for 循环、forEach、map、filter、reduce 等,帮助开发者更好地掌握这些技巧。


# 1. for 循环 vs forEach 方法对比

在JavaScript中,遍历数组是最常见的操作之一。for循环和forEach方法是两种主要的遍历方式,它们各有特点和适用场景。

# 1.1 基本语法对比

for循环的基本语法:

// for循环的基本结构
for(let i = 0; i < array.length; i++) {
    // 使用array[i]访问当前元素
    console.log(array[i]);
}
1
2
3
4
5

forEach方法的基本语法:

// forEach方法接收一个回调函数作为参数
array.forEach(function(item, index, array) {
    // item: 当前元素
    // index: 当前索引
    // array: 原始数组
    console.log(item);
});

// 使用箭头函数可以让代码更简洁
array.forEach(item => console.log(item));
1
2
3
4
5
6
7
8
9
10

# 1.2 两种方法的主要区别

特性 for循环 forEach方法
语法 传统、直观 现代、函数式
性能 较高(尤其是大数组) 较低(有函数调用开销)
可读性 代码较长 代码简洁清晰
中断循环 支持break/continue 不支持break/continue
适用场景 需要控制循环流程、性能敏感 简单遍历、代码简洁为主

# 1.3 性能比较实例

下面通过一个实际例子来比较两种方法的性能差异:

// 创建一个包含1000万个元素的大数组
let bigArray = [...Array(10000000).keys()]; // 生成0到9999999的数组
let total = 0;

// 使用for循环计时
console.log("开始for循环测试...");
let forStart = Date.now(); // 记录开始时间

// for循环遍历
for(let i = 0; i < bigArray.length; i++) {
    total += bigArray[i]; // 累加每个元素
}

let forEnd = Date.now(); // 记录结束时间
console.log(`for循环累加结果: ${total}`);
console.log(`for循环耗时: ${forEnd - forStart}毫秒`); // 显示执行时间

// 重置累加器
total = 0;

// 使用forEach计时
console.log("\n开始forEach测试...");
let forEachStart = Date.now(); // 记录开始时间

// forEach遍历
bigArray.forEach(item => {
    total += item; // 累加每个元素
});

let forEachEnd = Date.now(); // 记录结束时间
console.log(`forEach累加结果: ${total}`);
console.log(`forEach耗时: ${forEachEnd - forEachStart}毫秒`); // 显示执行时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

执行结果分析:

  • for循环通常比forEach快10%-30%(根据浏览器和数据量而异)
  • 数据量越大,性能差异越明显
  • 在小型数组(几百个元素)上,差异几乎可以忽略不计

# 1.4 循环控制能力对比

for循环最大的优势是可以灵活控制循环流程,而forEach则无法中断:

for循环可以随时中断:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let sum = 0;

// 使用for循环,可以在满足条件时提前退出
for(let i = 0; i < numbers.length; i++) {
    // 当累加和超过20时,立即停止循环
    if(sum > 20) {
        console.log("累加和已超过20,提前结束循环!");
        break; // 使用break跳出整个循环
    }
    
    // 跳过偶数(演示continue的使用)
    if(numbers[i] % 2 === 0) {
        console.log(`跳过偶数: ${numbers[i]}`);
        continue; // 跳过当前迭代,继续下一次循环
    }
    
    sum += numbers[i];
    console.log(`当前累加: ${numbers[i]}, 总和: ${sum}`);
}

console.log(`最终结果: ${sum}`);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

forEach无法中断循环:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let sum = 0;

// 使用forEach,无法真正中断循环
numbers.forEach(item => {
    // 尝试在累加和超过20时"中断"
    if(sum > 20) {
        console.log("累加和已超过20,但forEach会继续执行!");
        return; // 这里的return只能跳过当前元素的处理,不能终止整个循环
    }
    
    // 尝试跳过偶数
    if(item % 2 === 0) {
        console.log(`尝试跳过偶数: ${item}`);
        return; // 只能跳过当前元素
    }
    
    sum += item;
    console.log(`当前累加: ${item}, 总和: ${sum}`);
});

console.log(`最终结果: ${sum}`); // forEach会处理完所有元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 1.5 实际应用场景选择

适合使用for循环的场景:

  • 需要提前中断循环的场景(使用break)
  • 需要跳过某些迭代的场景(使用continue)
  • 需要同时操作多个数组
  • 性能关键场景,如处理大型数据集
  • 需要逆向遍历或按特定步长遍历

适合使用forEach的场景:

  • 简单的数组遍历,无需中断
  • 代码简洁性和可读性优先
  • 函数式编程风格的代码
  • 不需要关心索引或只需要元素值
  • 与其他数组方法(如map、filter)配合使用

# 2. map() 方法 —— 高效生成新数组

map()方法是JavaScript数组中最常用的遍历方法之一,它的主要特点是"映射"——将原数组的每个元素通过一个函数转换成新的元素,并返回一个全新的数组。原始数组保持不变,这符合函数式编程中的不可变性原则。

# 2.1 基本语法与工作原理

// map方法的基本语法
let newArray = array.map(function(currentValue, index, array) {
    // 返回新元素的值
    return 处理后的元素;
});

// 使用箭头函数的简洁写法
let newArray = array.map((currentValue, index, array) => 处理后的元素);
1
2
3
4
5
6
7
8

参数说明:

  • currentValue:当前正在处理的数组元素
  • index(可选):当前元素的索引
  • array(可选):调用map方法的原始数组
  • 返回值:一个新数组,每个元素都是回调函数处理原始数组元素后的结果

# 2.2 简单数值转换示例

下面是一个将数组中每个数字翻倍的简单例子:

// 原始数组
let numbers = [1, 2, 3, 4, 5];

// 使用map方法将每个元素乘以2
let doubledNumbers = numbers.map(num => num * 2);

// 查看结果
console.log("原始数组:", numbers);       // 输出: [1, 2, 3, 4, 5] - 原数组不变
console.log("新数组:", doubledNumbers);  // 输出: [2, 4, 6, 8, 10] - 每个元素都翻倍了
1
2
3
4
5
6
7
8
9

在这个例子中:

  1. 我们有一个包含5个数字的原始数组
  2. 使用map方法和一个简单的箭头函数num => num * 2处理每个元素
  3. 这个函数接收当前元素值,将其乘以2,然后返回结果
  4. map方法收集所有返回值,组成一个新数组返回
  5. 原始数组numbers保持不变

# 2.3 字符串处理示例

map方法不仅可以处理数字,还可以处理任何类型的数据,包括字符串:

// 一个包含名字的数组
let names = ["张三", "李四", "王五", "赵六"];

// 为每个名字添加"同学"后缀
let formalNames = names.map(name => {
    // 这里可以编写更复杂的转换逻辑
    return name + "同学"; // 返回处理后的字符串
});

console.log(formalNames); // 输出: ["张三同学", "李四同学", "王五同学", "赵六同学"]
1
2
3
4
5
6
7
8
9
10

# 2.4 对象数组处理(实际开发中最常见)

在实际开发中,我们经常需要处理对象数组,比如从API获取的数据。map方法在这种场景下特别有用:

// 假设这是从后端API获取的商品数据
let products = [
  { id: 101, name: "华为手机", price: 6999, stock: 15 },
  { id: 102, name: "苹果手机", price: 9888, stock: 10 },
  { id: 103, name: "小米手机", price: 2999, stock: 0 }
];

// 使用map处理数据,为前端展示做准备
let processedProducts = products.map(product => {
    // 返回一个新对象,包含我们需要的格式化数据
    return {
        ...product,                         // 保留原对象的所有属性
        price: product.price + "元",        // 价格后面添加"元"
        priceUSD: (product.price / 7).toFixed(2) + "$", // 添加美元价格
        inStock: product.stock > 0,         // 添加库存状态布尔值
        stockStatus: product.stock > 0 ? "有货" : "缺货" // 添加中文库存状态
    };
});

console.log(processedProducts);
/* 输出:
[
  {
    id: 101,
    name: "华为手机",
    price: "6999元",
    stock: 15,
    priceUSD: "999.86$",
    inStock: true,
    stockStatus: "有货"
  },
  {
    id: 102,
    name: "苹果手机",
    price: "9888元",
    stock: 10,
    priceUSD: "1412.57$",
    inStock: true,
    stockStatus: "有货"
  },
  {
    id: 103,
    name: "小米手机",
    price: "2999元",
    stock: 0,
    priceUSD: "428.43$",
    inStock: false,
    stockStatus: "缺货"
  }
]
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

在这个例子中:

  1. 我们有一个包含商品信息的对象数组
  2. 使用map方法处理每个商品对象
  3. 对于每个对象,我们:
    • 使用扩展运算符(...)保留原有属性
    • 修改价格格式,添加"元"单位
    • 计算并添加美元价格
    • 添加库存状态的布尔值和中文描述
  4. 最终得到一个包含更多信息、更适合前端展示的新数组

# 2.5 使用索引参数

map方法的回调函数还可以接收当前元素的索引,这在某些场景下非常有用:

// 一个简单的数组
let letters = ["a", "b", "c", "d"];

// 使用map方法,同时利用元素值和索引
let result = letters.map((letter, index) => {
    // 返回一个包含元素和其位置的字符串
    return `第${index+1}个字母是${letter.toUpperCase()}`;
});

console.log(result);
// 输出: ["第1个字母是A", "第2个字母是B", "第3个字母是C", "第4个字母是D"]
1
2
3
4
5
6
7
8
9
10
11

# 2.6 map()方法的实际应用场景

  1. 数据转换:将后端API返回的数据转换为前端需要的格式
  2. 表单处理:处理用户输入的数据
  3. 数据可视化:转换原始数据为图表所需格式
  4. 列表渲染:在React、Vue等框架中渲染列表项
  5. 批量元素操作:同时修改多个元素的属性

map()方法的注意事项

  1. 返回新数组:map总是返回一个新数组,原数组不会被修改
  2. 数组长度不变:新数组的长度与原数组相同
  3. 必须有返回值:回调函数必须有返回值,否则新数组对应位置会是undefined
  4. 性能考虑:对于大型数组,如果不需要返回值,考虑使用forEach

# 2.7 与其他数组方法的组合使用

map方法经常与其他数组方法组合使用,形成强大的数据处理管道:

// 一个包含各种数字的数组
let mixedNumbers = [1, -5, 3, 0, -2, 7, -8, 4];

// 先过滤出正数,然后将每个正数平方
let squaredPositives = mixedNumbers
    .filter(num => num > 0)    // 先过滤出所有正数
    .map(num => num * num);    // 然后对每个正数求平方

console.log(squaredPositives); // 输出: [1, 9, 49, 16]
1
2
3
4
5
6
7
8
9

在这个例子中,我们先使用filter方法过滤出所有正数,然后使用map方法计算每个正数的平方。这种链式调用是函数式编程的典型应用,代码简洁且易于理解。

小贴士

map()方法是函数式编程的核心概念之一,掌握它不仅能让你的代码更简洁,还能帮助你更好地理解React、Vue等现代前端框架中的数据处理模式。


# 3. filter() 方法 —— 过滤数组中的元素

filter()方法是JavaScript数组中非常实用的一个方法,它的主要作用是"筛选"——根据指定的条件,从原数组中筛选出符合条件的元素,并创建一个新数组返回。原始数组不会被修改,这使得我们可以安全地对数据进行筛选操作。

# 3.1 基本语法与工作原理

// filter方法的基本语法
let newArray = array.filter(function(currentValue, index, array) {
    // 返回布尔值:true表示保留该元素,false表示过滤掉该元素
    return 条件表达式;
});

// 使用箭头函数的简洁写法
let newArray = array.filter((currentValue, index, array) => 条件表达式);
1
2
3
4
5
6
7
8

参数说明:

  • currentValue:当前正在处理的数组元素
  • index(可选):当前元素的索引
  • array(可选):调用filter方法的原始数组
  • 返回值:一个新数组,包含所有满足条件的元素(回调函数返回true的元素)

# 3.2 简单数值过滤示例

下面是一个过滤出数组中所有偶数的简单例子:

// 原始数组
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 使用filter方法过滤出所有偶数
let evenNumbers = numbers.filter(num => {
    // 条件:如果num能被2整除(余数为0),则为偶数
    return num % 2 === 0;
    
    // 也可以简写为:return num % 2 === 0;
});

// 查看结果
console.log("原始数组:", numbers);       // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - 原数组不变
console.log("偶数数组:", evenNumbers);   // 输出: [2, 4, 6, 8, 10] - 只包含偶数
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在这个例子中:

  1. 我们有一个包含1到10的原始数组
  2. 使用filter方法和一个简单的箭头函数num => num % 2 === 0作为条件
  3. 这个函数检查每个数字是否能被2整除(是否为偶数)
  4. 如果条件为true,该元素会被保留在新数组中
  5. 如果条件为false,该元素会被过滤掉
  6. 最终返回一个只包含偶数的新数组
  7. 原始数组numbers保持不变

# 3.3 多条件过滤示例

filter方法可以使用复杂的条件组合,例如同时满足多个条件:

// 一个包含各种数字的数组
let numbers = [1, 5, 8, 12, 15, 20, 25, 30, 35, 40];

// 过滤出同时满足两个条件的数字:大于10且能被5整除
let filteredNumbers = numbers.filter(num => {
    // 条件1:大于10
    let isGreaterThan10 = num > 10;
    
    // 条件2:能被5整除
    let isDivisibleBy5 = num % 5 === 0;
    
    // 同时满足两个条件
    return isGreaterThan10 && isDivisibleBy5;
    
    // 也可以简写为:return num > 10 && num % 5 === 0;
});

console.log(filteredNumbers); // 输出: [15, 20, 25, 30, 35, 40]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 3.4 对象数组过滤(实际开发中最常用)

在实际开发中,我们经常需要从对象数组中筛选出符合特定条件的对象,这是filter方法最常见的应用场景:

// 假设这是从后端API获取的用户数据
let users = [
  { id: 1, name: "张三", age: 28, active: true },
  { id: 2, name: "李四", age: 17, active: true },
  { id: 3, name: "王五", age: 32, active: false },
  { id: 4, name: "赵六", age: 15, active: true },
  { id: 5, name: "钱七", age: 45, active: false }
];

// 筛选出所有成年且账户处于活跃状态的用户
let activeAdults = users.filter(user => {
    // 条件1:年龄大于等于18(成年)
    let isAdult = user.age >= 18;
    
    // 条件2:账户状态为活跃
    let isActive = user.active === true;
    
    // 同时满足两个条件
    return isAdult && isActive;
});

console.log(activeAdults);
/* 输出:
[
  { id: 1, name: "张三", age: 28, active: true }
]
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

在这个例子中,我们从用户数组中筛选出同时满足"成年"和"账户活跃"两个条件的用户。

# 3.5 使用索引和原数组参数

filter方法的回调函数还可以接收当前元素的索引和原始数组,这在某些场景下非常有用:

// 一个包含重复元素的数组
let fruits = ["苹果", "香蕉", "苹果", "橙子", "香蕉", "苹果"];

// 过滤出数组中第一次出现的每种水果(去重)
let uniqueFruits = fruits.filter((fruit, index, array) => {
    // 检查当前元素在数组中第一次出现的位置是否就是当前索引
    // 如果是,说明这是该元素第一次出现
    return array.indexOf(fruit) === index;
});

console.log(uniqueFruits); // 输出: ["苹果", "香蕉", "橙子"]
1
2
3
4
5
6
7
8
9
10
11

在这个例子中,我们使用indexOf方法找到每个元素在数组中第一次出现的位置,然后与当前索引比较。如果相等,说明这是该元素第一次出现,我们保留它;如果不相等,说明这是重复元素,我们过滤掉它。

# 3.6 过滤空值和无效数据

在处理实际数据时,我们经常需要过滤掉空值、null、undefined等无效数据:

// 一个包含各种数据的数组,包括一些无效值
let mixedData = [0, 1, false, 2, "", 3, null, undefined, 4, NaN];

// 过滤出所有有效值(排除false、0、""、null、undefined和NaN)
let validValues = mixedData.filter(item => {
    // Boolean(item)会将有效值转换为true,无效值转换为false
    return Boolean(item);
    
    // 也可以简写为:return Boolean(item);
    // 或者更简洁地写为:return item;
});

console.log(validValues); // 输出: [1, 2, 3, 4]
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3.7 在实际项目中的应用场景

  1. 数据筛选:根据用户选择的条件筛选商品列表
  2. 表单验证:过滤出所有未通过验证的表单字段
  3. 权限控制:筛选出用户有权访问的功能或页面
  4. 数据清洗:去除API返回数据中的无效或不需要的记录
  5. 搜索功能:根据用户输入的关键词筛选匹配的项目

# 3.8 与其他数组方法的组合使用

filter方法经常与其他数组方法组合使用,形成强大的数据处理流程:

// 一个商品数组
let products = [
  { id: 1, name: "手机", price: 2999, category: "电子" },
  { id: 2, name: "笔记本", price: 8999, category: "电子" },
  { id: 3, name: "书籍", price: 58, category: "图书" },
  { id: 4, name: "键盘", price: 299, category: "电子" },
  { id: 5, name: "鼠标", price: 99, category: "电子" }
];

// 先筛选出电子类商品,然后按价格从低到高排序,最后提取商品名称
let sortedElectronics = products
  .filter(product => product.category === "电子") // 筛选电子类商品
  .sort((a, b) => a.price - b.price)             // 按价格升序排序
  .map(product => product.name);                 // 提取商品名称

console.log(sortedElectronics); // 输出: ["鼠标", "键盘", "手机", "笔记本"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在这个例子中,我们首先使用filter筛选出所有电子类商品,然后使用sort方法按价格排序,最后使用map方法提取商品名称。这种链式调用使代码简洁且易于理解。

小贴士

filter()方法是数据处理的强大工具,尤其在处理API返回的数据时非常有用。它可以帮助你快速去除无效数据,或者根据用户需求筛选出符合条件的数据子集。


# 4. reduce() 方法 —— 数组求和与聚合

reduce()方法是JavaScript数组中最强大也最灵活的方法之一,它的主要作用是将数组"归纳"为单个值。与其他数组方法不同,reduce()不仅可以处理数字求和,还能处理复杂的数据转换和聚合操作,是处理数据的瑞士军刀。

# 4.1 基本语法与工作原理

// reduce方法的基本语法
let result = array.reduce(function(累加器, 当前值, 当前索引, 原数组) {
    // 处理逻辑,返回新的累加器值
    return 新的累加器值;
}, 初始值);

// 使用箭头函数的简洁写法
let result = array.reduce((累加器, 当前值) => 新的累加器值, 初始值);
1
2
3
4
5
6
7
8

参数详解:

  • 累加器(accumulator):存储累积结果的变量,每次迭代都会更新
  • 当前值(currentValue):当前正在处理的数组元素
  • 当前索引(index):可选参数,当前元素的索引
  • 原数组(array):可选参数,调用reduce的原始数组
  • 初始值(initialValue):可选参数,第一次调用回调函数时累加器的初始值

# 4.2 理解reduce的工作流程

reduce()方法的工作流程可以想象成一个"雪球效应":从一个初始值开始,随着遍历数组中的每个元素,这个"雪球"(累加器)不断变大或变化,最终形成我们想要的结果。

reduce工作流程示意图

# 4.3 简单数值求和示例(详细解析)

下面通过一个简单的数组求和例子,详细解释reduce()的工作过程:

// 一个简单的数字数组
let numbers = [1, 2, 3, 4, 5];

// 使用reduce计算数组所有元素的和
let sum = numbers.reduce((累加器, 当前值) => {
    console.log(`当前累加器值: ${累加器}, 当前元素值: ${当前值}`);
    
    // 将当前元素值加到累加器上
    let 新累加器 = 累加器 + 当前值;
    
    console.log(`计算后的新累加器值: ${新累加器}`);
    return 新累加器;
}, 0); // 初始值设为0

console.log(`最终结果: ${sum}`); // 输出: 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

执行过程详解:

  1. 初始状态:累加器 = 0(我们提供的初始值)
  2. 第一次迭代:
    • 累加器 = 0
    • 当前值 = 1(数组的第一个元素)
    • 计算:0 + 1 = 1
    • 返回新累加器值:1
  3. 第二次迭代:
    • 累加器 = 1(上一次迭代的返回值)
    • 当前值 = 2(数组的第二个元素)
    • 计算:1 + 2 = 3
    • 返回新累加器值:3
  4. 第三次迭代:
    • 累加器 = 3(上一次迭代的返回值)
    • 当前值 = 3(数组的第三个元素)
    • 计算:3 + 3 = 6
    • 返回新累加器值:6
  5. 第四次迭代:
    • 累加器 = 6(上一次迭代的返回值)
    • 当前值 = 4(数组的第四个元素)
    • 计算:6 + 4 = 10
    • 返回新累加器值:10
  6. 第五次迭代:
    • 累加器 = 10(上一次迭代的返回值)
    • 当前值 = 5(数组的第五个元素)
    • 计算:10 + 5 = 15
    • 返回新累加器值:15
  7. 最终结果:15

# 4.4 初始值的重要性

提供初始值是一个好习惯,它可以避免空数组导致的错误,并且使代码逻辑更清晰:

// 不提供初始值的情况
let numbers = [1, 2, 3, 4, 5];
let sum1 = numbers.reduce((累加器, 当前值) => 累加器 + 当前值);
// 第一次迭代时,累加器=1(第一个元素),当前值=2(第二个元素)

// 提供初始值的情况
let sum2 = numbers.reduce((累加器, 当前值) => 累加器 + 当前值, 0);
// 第一次迭代时,累加器=0(初始值),当前值=1(第一个元素)

console.log(sum1); // 15
console.log(sum2); // 15

// 空数组的情况
let emptyArray = [];
// 不提供初始值会报错
// let errorSum = emptyArray.reduce((累加器, 当前值) => 累加器 + 当前值);
// 提供初始值则安全
let safeSum = emptyArray.reduce((累加器, 当前值) => 累加器 + 当前值, 0);
console.log(safeSum); // 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 4.5 对象数组的属性累加(实际开发常用)

在实际开发中,我们经常需要计算对象数组中某个属性的总和,例如购物车商品的总价:

// 购物车商品数组
let cart = [
    { name: "iPhone", price: 8999, quantity: 1 },
    { name: "iPad", price: 6999, quantity: 2 },
    { name: "AirPods", price: 1999, quantity: 3 }
];

// 计算购物车总价
let totalPrice = cart.reduce((总价, 当前商品) => {
    // 当前商品总价 = 单价 × 数量
    let 商品价格 = 当前商品.price * 当前商品.quantity;
    
    console.log(`添加商品: ${当前商品.name}, 价格: ${商品价格}元`);
    
    // 返回累加后的总价
    return 总价 + 商品价格;
}, 0);

console.log(`购物车总价: ${totalPrice}元`); // 输出: 23994元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 4.6 数组扁平化(多维数组转一维)

reduce()方法可以优雅地实现数组扁平化,将多维数组转换为一维数组:

// 一个二维数组
let nestedArrays = [[1, 2], [3, 4], [5, 6]];

// 使用reduce将二维数组扁平化为一维数组
let flatArray = nestedArrays.reduce((累加器, 当前数组) => {
    // 将当前数组的所有元素添加到累加器中
    // concat方法不会修改原数组,而是返回一个新数组
    return 累加器.concat(当前数组);
}, []);

console.log(flatArray); // 输出: [1, 2, 3, 4, 5, 6]
1
2
3
4
5
6
7
8
9
10
11

# 4.7 分组统计(高级应用)

reduce()方法非常适合用于分组统计,例如统计数组中各元素出现的次数:

// 一个水果数组
let fruits = ['苹果', '香蕉', '苹果', '橙子', '香蕉', '苹果', '梨'];

// 使用reduce统计每种水果出现的次数
let fruitCount = fruits.reduce((统计结果, 当前水果) => {
    // 如果当前水果已经在统计结果中,次数加1
    // 否则,初始化为1
    if (当前水果 in 统计结果) {
        统计结果[当前水果]++;
    } else {
        统计结果[当前水果] = 1;
    }
    
    return 统计结果;
}, {}); // 初始值为空对象

console.log(fruitCount);
// 输出: { '苹果': 3, '香蕉': 2, '橙子': 1, '梨': 1 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4.8 链式操作与管道处理

reduce()方法可以与其他数组方法结合,形成强大的数据处理管道:

// 一个包含学生成绩的数组
let students = [
    { name: '张三', scores: [85, 90, 65] },
    { name: '李四', scores: [70, 75, 60] },
    { name: '王五', scores: [95, 88, 92] }
];

// 计算所有学生的平均分,并找出平均分最高的学生
let topStudent = students
    .map(student => {
        // 计算每个学生的平均分
        let totalScore = student.scores.reduce((sum, score) => sum + score, 0);
        let averageScore = totalScore / student.scores.length;
        
        // 返回包含学生姓名和平均分的新对象
        return { name: student.name, average: averageScore };
    })
    .reduce((最高分学生, 当前学生) => {
        // 如果当前学生平均分更高,则更新最高分学生
        if (当前学生.average > 最高分学生.average) {
            return 当前学生;
        } else {
            return 最高分学生;
        }
    });

console.log(`平均分最高的学生是${topStudent.name},平均分为${topStudent.average.toFixed(2)}分`);
// 输出: 平均分最高的学生是王五,平均分为91.67分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 4.9 实现其他数组方法

有趣的是,reduce()方法非常强大,以至于可以用它来实现其他数组方法,如map()、filter()等:

// 使用reduce实现map功能
let numbers = [1, 2, 3, 4, 5];

// 原生map方法
let doubled1 = numbers.map(num => num * 2);

// 使用reduce实现map功能
let doubled2 = numbers.reduce((新数组, 当前值) => {
    新数组.push(当前值 * 2); // 将处理后的值添加到新数组
    return 新数组;
}, []);

console.log(doubled1); // [2, 4, 6, 8, 10]
console.log(doubled2); // [2, 4, 6, 8, 10]

// 使用reduce实现filter功能
// 原生filter方法
let evens1 = numbers.filter(num => num % 2 === 0);

// 使用reduce实现filter功能
let evens2 = numbers.reduce((新数组, 当前值) => {
    if (当前值 % 2 === 0) {
        新数组.push(当前值); // 只添加符合条件的值
    }
    return 新数组;
}, []);

console.log(evens1); // [2, 4]
console.log(evens2); // [2, 4]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 4.10 注意事项与最佳实践

  1. 始终提供初始值:避免空数组导致的错误,并使逻辑更清晰
  2. 保持回调函数的纯净:避免在回调函数中修改外部变量或产生副作用
  3. 适当拆分复杂逻辑:如果reduce()操作很复杂,考虑拆分为多个步骤
  4. 注意性能:对于非常大的数组,reduce()可能不是最高效的选择
  5. 可读性优先:有时候用for循环可能更易读,不要为了使用reduce()而使代码变得晦涩

实际应用场景

  1. 数值计算:求和、求积、求平均值、求最大/最小值
  2. 数据转换:数组转对象、对象转数组、数组扁平化
  3. 数据分组:按属性分组、统计频次
  4. 数据筛选与合并:复杂条件的筛选与合并
  5. 链式数据处理:与其他数组方法结合形成数据处理管道

小贴士

reduce()方法初学时可能有些难以理解,但掌握后将成为你处理复杂数据的得力助手。记住,它的核心思想是"积累"——将数组中的所有元素通过某种方式积累成一个最终结果。


# 5. every() 方法 —— 全部满足条件时返回 true

every()方法是JavaScript数组的一个实用方法,它的作用类似于"全体检查"——检查数组中的每一个元素是否都满足特定条件。可以把它想象成一次"全员考核",只有当所有成员都通过考核时,才返回true,否则返回false。

# 5.1 基本语法与工作原理

// every方法的基本语法
let result = array.every(function(当前元素, 索引, 原数组) {
    // 返回布尔值:true或false
    return 条件判断;
});

// 使用箭头函数的简洁写法
let result = array.every((当前元素) => 条件判断);
1
2
3
4
5
6
7
8

参数说明:

  • 当前元素:数组中正在处理的当前元素
  • 索引(可选):当前元素在数组中的索引
  • 原数组(可选):调用every方法的原始数组
  • 返回值:如果所有元素都满足条件,返回true;只要有一个元素不满足条件,立即返回false

# 5.2 工作流程图解

every()方法的工作流程可以用下面的流程图来表示:

开始
 ↓
取第一个元素
 ↓
应用条件判断
 ↓
不满足条件 → 立即返回false(整个方法结束)
 ↓
满足条件
 ↓
还有更多元素吗?→ 否 → 返回true(所有元素都满足条件)
 ↓ 是
取下一个元素
 ↓
(重复上述步骤)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5.3 简单示例:检查数组是否全为正数

下面是一个检查数组中所有元素是否都为正数的例子:

// 一个包含数字的数组
let numbers = [1, 2, 3, 4, 5];

// 使用every方法检查是否所有元素都大于0
let allPositive = numbers.every(num => {
    console.log(`检查元素: ${num}`);
    return num > 0; // 条件:元素必须大于0才是正数
});

console.log(`所有元素都是正数吗?${allPositive}`); // 输出: true

// 再试一个包含负数的数组
let mixedNumbers = [1, 2, -3, 4, 5];
let allPositive2 = mixedNumbers.every(num => {
    console.log(`检查元素: ${num}`);
    return num > 0;
});

console.log(`所有元素都是正数吗?${allPositive2}`); // 输出: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

在第二个例子中,一旦every()方法遇到-3这个不满足条件的元素,就会立即返回false,不再继续检查后面的元素。这是every()方法的一个重要特性,称为"短路求值"。

# 5.4 对象数组的条件检查(实际应用)

在实际开发中,我们经常需要检查对象数组中的所有对象是否都满足某个条件:

// 一个商品库存数组
let products = [
    { name: "华为手机", price: 5999, inStock: true },
    { name: "苹果手机", price: 8999, inStock: true },
    { name: "小米手机", price: 3999, inStock: true }
];

// 检查是否所有商品都有库存
let allInStock = products.every(product => {
    console.log(`检查商品: ${product.name}`);
    return product.inStock === true; // 条件:库存状态必须为true
});

console.log(`所有商品都有库存吗?${allInStock}`); // 输出: true

// 添加一个缺货商品后再检查
products.push({ name: "游戏手柄", price: 399, inStock: false });

let stillAllInStock = products.every(product => {
    console.log(`检查商品: ${product.name}`);
    return product.inStock === true;
});

console.log(`所有商品都有库存吗?${stillAllInStock}`); // 输出: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

在这个例子中,我们从用户数组中筛选出同时满足"成年"和"账户活跃"两个条件的用户。

# 5.5 复杂条件组合

every()方法可以使用复杂的条件组合,例如同时检查多个条件:

// 一个学生成绩数组
let students = [
    { name: "张三", score: 85, attendance: 0.95 },
    { name: "李四", score: 92, attendance: 0.88 },
    { name: "王五", score: 78, attendance: 0.97 }
];

// 检查是否所有学生都达到了及格标准(分数>=60且出勤率>=0.8)
let allQualified = students.every(student => {
    // 条件1:分数必须大于等于60分
    let scoreQualified = student.score >= 60;
    
    // 条件2:出勤率必须大于等于80%
    let attendanceQualified = student.attendance >= 0.8;
    
    // 两个条件必须同时满足
    let isQualified = scoreQualified && attendanceQualified;
    
    console.log(`学生: ${student.name}, 分数合格: ${scoreQualified}, 出勤合格: ${attendanceQualified}`);
    
    return isQualified;
});

console.log(`所有学生都达到及格标准吗?${allQualified}`); // 输出: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 5.6 与其他数组方法的比较

every()方法与some()方法是一对互补的方法:

  • every():所有元素都满足条件才返回true(类似逻辑与 AND)
  • some():只要有一个元素满足条件就返回true(类似逻辑或 OR)
let numbers = [1, 2, 3, 4, 5];

// 使用every检查是否所有元素都大于3
let allGreaterThan3 = numbers.every(num => num > 3);
console.log(`所有元素都大于3吗?${allGreaterThan3}`); // 输出: false

// 使用some检查是否有元素大于3
let someGreaterThan3 = numbers.some(num => num > 3);
console.log(`有元素大于3吗?${someGreaterThan3}`); // 输出: true
1
2
3
4
5
6
7
8
9

# 5.7 实际应用场景

  1. 表单验证:检查所有必填字段是否都已填写
  2. 权限检查:验证用户是否拥有执行某操作所需的所有权限
  3. 数据验证:确保数据集中的所有记录都符合特定标准
  4. 条件过滤:在应用复杂过滤条件前,先快速检查是否有必要进行过滤
  5. 测试用例:检查一组测试用例是否全部通过

# 5.8 注意事项

  1. 空数组:对空数组调用every()方法会返回true(这是一个"空集合"的数学特性)
  2. 短路求值:一旦找到不满足条件的元素,立即返回false,不再继续检查
  3. 不修改原数组:every()方法不会修改原始数组
  4. 回调函数中的this:如果需要在回调函数中使用this,请使用常规函数而非箭头函数,并传入this值
// 空数组示例
let emptyArray = [];
console.log(emptyArray.every(item => item > 10)); // 输出: true(空集合特性)

// 使用this的示例
let validator = {
    min: 10,
    check: function(array) {
        return array.every(function(item) {
            return item > this.min; // 这里的this指向validator对象
        }, this); // 传入this作为上下文
    }
};

console.log(validator.check([11, 12, 13])); // 输出: true
console.log(validator.check([9, 12, 13]));  // 输出: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

小贴士

every()方法是进行"全员检查"的理想选择,特别适合需要确保所有元素都符合某个标准的场景。记住它的核心特点:一票否决制——只要有一个元素不满足条件,整个结果就是false。


# 6. some() 方法 —— 至少一个元素满足条件时返回 true

some()方法是JavaScript数组的一个实用方法,它的作用类似于"查找队伍中的特殊人才"——只要数组中有一个元素满足条件,就返回true。可以把它想象成"一个满足条件就够了"的场景,比如在一群人中找一个会说英语的人,只要找到一个就可以了。

# 6.1 基本语法与工作原理

// some方法的基本语法
let result = array.some(function(当前元素, 索引, 原数组) {
    // 返回布尔值:true或false
    return 条件判断;
});

// 使用箭头函数的简洁写法
let result = array.some((当前元素) => 条件判断);
1
2
3
4
5
6
7
8

参数说明:

  • 当前元素:数组中正在处理的当前元素
  • 索引(可选):当前元素在数组中的索引
  • 原数组(可选):调用some方法的原始数组
  • 返回值:如果至少有一个元素满足条件,返回true;如果所有元素都不满足条件,返回false

# 6.2 工作流程图解

some()方法的工作流程可以用下面的流程图来表示:

开始
 ↓
取第一个元素
 ↓
应用条件判断
 ↓
满足条件 → 立即返回true(整个方法结束)
 ↓
不满足条件
 ↓
还有更多元素吗?→ 否 → 返回false(没有元素满足条件)
 ↓ 是
取下一个元素
 ↓
(重复上述步骤)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 6.3 简单示例:检查数组中是否有偶数

下面是一个检查数组中是否存在偶数的例子:

// 一个包含数字的数组
let numbers = [1, 3, 5, 7, 8, 9];

// 使用some方法检查是否存在偶数
let hasEven = numbers.some(num => {
    console.log(`检查元素: ${num}`);
    return num % 2 === 0; // 条件:能被2整除的是偶数
});

console.log(`数组中有偶数吗?${hasEven}`); // 输出: true

// 再试一个只包含奇数的数组
let oddNumbers = [1, 3, 5, 7, 9];
let hasEven2 = oddNumbers.some(num => {
    console.log(`检查元素: ${num}`);
    return num % 2 === 0;
});

console.log(`数组中有偶数吗?${hasEven2}`); // 输出: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

在第一个例子中,一旦some()方法遇到8这个满足条件的元素,就会立即返回true,不再继续检查后面的元素。这是some()方法的一个重要特性,称为"短路求值"。

# 6.4 对象数组的条件检查(实际应用)

在实际开发中,我们经常需要检查对象数组中是否存在满足某个条件的对象:

// 一个商品库存数组
let products = [
    { name: "华为手机", price: 5999, inStock: true },
    { name: "苹果手机", price: 8999, inStock: true },
    { name: "小米手机", price: 3999, inStock: false },
    { name: "OPPO手机", price: 4999, inStock: true }
];

// 检查是否有缺货商品
let anyOutOfStock = products.some(product => {
    console.log(`检查商品: ${product.name}`);
    return product.inStock === false; // 条件:库存状态为false表示缺货
});

console.log(`有缺货商品吗?${anyOutOfStock}`); // 输出: true

// 检查是否有低于3000元的商品
let anyLowPrice = products.some(product => {
    console.log(`检查商品价格: ${product.name} - ${product.price}元`);
    return product.price < 3000;
});

console.log(`有低于3000元的商品吗?${anyLowPrice}`); // 输出: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 6.5 复杂条件组合

some()方法可以使用复杂的条件组合,例如同时检查多个条件:

// 一个学生成绩数组
let students = [
    { name: "张三", score: 85, major: "计算机" },
    { name: "李四", score: 92, major: "数学" },
    { name: "王五", score: 78, major: "物理" },
    { name: "赵六", score: 96, major: "计算机" }
];

// 检查是否有计算机专业且分数超过90分的学生
let hasTopCSStudent = students.some(student => {
    // 条件1:是计算机专业
    let isCSMajor = student.major === "计算机";
    
    // 条件2:分数超过90分
    let isTopScore = student.score > 90;
    
    // 两个条件必须同时满足
    let isQualified = isCSMajor && isTopScore;
    
    console.log(`学生: ${student.name}, 专业: ${student.major}, 分数: ${student.score}, 符合条件: ${isQualified}`);
    
    return isQualified;
});

console.log(`有计算机专业且分数超过90分的学生吗?${hasTopCSStudent}`); // 输出: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 6.6 与其他数组方法的比较

some()方法与every()方法是一对互补的方法:

  • some():只要有一个元素满足条件就返回true(类似逻辑或 OR)
  • every():所有元素都满足条件才返回true(类似逻辑与 AND)
let numbers = [1, 2, 3, 4, 5];

// 使用some检查是否有元素大于4
let someGreaterThan4 = numbers.some(num => num > 4);
console.log(`有元素大于4吗?${someGreaterThan4}`); // 输出: true

// 使用every检查是否所有元素都大于4
let allGreaterThan4 = numbers.every(num => num > 4);
console.log(`所有元素都大于4吗?${allGreaterThan4}`); // 输出: false
1
2
3
4
5
6
7
8
9

some()方法与find()和includes()方法的区别:

  • some():检查是否存在满足条件的元素,返回布尔值
  • find():返回第一个满足条件的元素本身,没有则返回undefined
  • includes():检查是否包含特定值,不能使用自定义条件函数
let numbers = [1, 2, 3, 4, 5];

// 使用some检查是否有大于3的元素
console.log(numbers.some(num => num > 3)); // 输出: true

// 使用find找出第一个大于3的元素
console.log(numbers.find(num => num > 3)); // 输出: 4

// 使用includes检查是否包含数字3
console.log(numbers.includes(3)); // 输出: true
1
2
3
4
5
6
7
8
9
10

# 6.7 实际应用场景

  1. 权限验证:检查用户是否拥有某个特定权限
  2. 表单验证:检查是否有任何表单字段未通过验证
  3. 数据筛选:在应用复杂过滤前,先快速检查是否有符合条件的数据
  4. 特征检测:检查数组中是否存在具有特定特征的元素
  5. 错误检查:检查一组操作中是否有任何一个出错

实用示例:用户权限检查

// 用户权限数组
let userPermissions = ["read", "comment", "like"];

// 需要的权限
let requiredPermissions = ["edit", "delete", "admin"];

// 检查用户是否拥有任何一个所需权限
function hasAnyRequiredPermission(userPerms, requiredPerms) {
    return requiredPerms.some(permission => {
        // 检查用户权限数组中是否包含当前所需权限
        let hasPermission = userPerms.includes(permission);
        console.log(`检查权限: ${permission} - ${hasPermission ? '有权限' : '无权限'}`);
        return hasPermission;
    });
}

let canAccess = hasAnyRequiredPermission(userPermissions, requiredPermissions);
console.log(`用户可以访问吗?${canAccess}`); // 输出: false

// 添加一个所需权限后再检查
userPermissions.push("edit");
canAccess = hasAnyRequiredPermission(userPermissions, requiredPermissions);
console.log(`添加权限后,用户可以访问吗?${canAccess}`); // 输出: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 6.8 注意事项

  1. 空数组:对空数组调用some()方法会返回false(这是一个"空集合"的数学特性)
  2. 短路求值:一旦找到满足条件的元素,立即返回true,不再继续检查
  3. 不修改原数组:some()方法不会修改原始数组
  4. 回调函数中的this:如果需要在回调函数中使用this,请使用常规函数而非箭头函数,并传入this值
// 空数组示例
let emptyArray = [];
console.log(emptyArray.some(item => item > 10)); // 输出: false(空集合特性)

// 使用this的示例
let checker = {
    threshold: 10,
    check: function(array) {
        return array.some(function(item) {
            return item > this.threshold; // 这里的this指向checker对象
        }, this); // 传入this作为上下文
    }
};

console.log(checker.check([5, 8, 12])); // 输出: true
console.log(checker.check([5, 8, 9]));  // 输出: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

小贴士

some()方法是进行"存在性检查"的理想选择,特别适合需要确认"是否存在至少一个符合条件的元素"的场景。记住它的核心特点:一个满足就行——只要有一个元素满足条件,整个结果就是true。


# 7. includes() 方法 —— 检查数组是否包含特定值

includes()方法是JavaScript数组中一个简单而实用的方法,它的作用就像是在一堆东西中"寻宝"——检查数组中是否包含某个特定的元素。这个方法返回一个布尔值:找到了返回true,没找到返回false。

# 7.1 基本语法与工作原理

// includes方法的基本语法
let result = array.includes(searchElement, fromIndex);
1
2

参数说明:

  • searchElement:要查找的元素
  • fromIndex(可选):从哪个位置开始查找,默认为0(即从头开始)
  • 返回值:如果找到指定元素,返回true;否则返回false

# 7.2 简单示例:检查数组中是否包含某个数值

// 一个简单的数字数组
let numbers = [1, 2, 3, 4, 5];

// 检查数组是否包含数字3
let hasThree = numbers.includes(3);
console.log(`数组中包含数字3吗?${hasThree}`); // 输出: true

// 检查数组是否包含数字6
let hasSix = numbers.includes(6);
console.log(`数组中包含数字6吗?${hasSix}`); // 输出: false
1
2
3
4
5
6
7
8
9
10

# 7.3 使用第二个参数:指定查找的起始位置

includes()方法的第二个参数允许我们指定从哪个索引位置开始查找:

let fruits = ["苹果", "香蕉", "橙子", "苹果", "梨"];

// 从头开始查找"苹果"
console.log(fruits.includes("苹果")); // 输出: true

// 从索引1开始查找"苹果"(跳过第一个"苹果")
console.log(fruits.includes("苹果", 1)); // 输出: true(因为索引3还有一个"苹果")

// 从索引4开始查找"苹果"
console.log(fruits.includes("苹果", 4)); // 输出: false(索引4及之后没有"苹果")
1
2
3
4
5
6
7
8
9
10

负数索引:如果提供负数作为第二个参数,它会被当作数组末尾的偏移量:

let letters = ["a", "b", "c", "d", "e"];

// 从倒数第三个元素开始查找(相当于从索引2开始)
console.log(letters.includes("c", -3)); // 输出: true
console.log(letters.includes("a", -3)); // 输出: false(从"c"开始查找,找不到"a")
1
2
3
4
5

# 7.4 与字符串比较

includes()方法在比较时区分大小写,并且使用严格相等(===)进行比较:

let words = ["hello", "world", "javascript"];

// 区分大小写
console.log(words.includes("hello")); // 输出: true
console.log(words.includes("Hello")); // 输出: false(大小写不匹配)

// 数字和字符串比较
let mixed = [1, "2", 3];
console.log(mixed.includes(2));    // 输出: false(没有数字2)
console.log(mixed.includes("2"));  // 输出: true(有字符串"2")
1
2
3
4
5
6
7
8
9
10

# 7.5 与对象和数组比较

includes()方法在比较对象和数组时,会检查它们是否是同一个引用,而不是它们的内容是否相同:

// 对象比较
let obj1 = { name: "张三" };
let obj2 = { name: "张三" }; // 内容相同但是不同的对象
let objects = [obj1];

console.log(objects.includes(obj1)); // 输出: true(找到了同一个对象引用)
console.log(objects.includes(obj2)); // 输出: false(虽然内容相同,但是不同的对象引用)

// 数组比较
let arr1 = [1, 2];
let arr2 = [1, 2]; // 内容相同但是不同的数组
let arrays = [arr1];

console.log(arrays.includes(arr1)); // 输出: true(找到了同一个数组引用)
console.log(arrays.includes(arr2)); // 输出: false(虽然内容相同,但是不同的数组引用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 7.6 与其他查找方法的比较

JavaScript提供了多种查找数组元素的方法,它们各有特点:

  1. includes():检查是否包含特定值,返回布尔值
  2. indexOf():查找特定值的索引,找不到返回-1
  3. find():查找满足条件的第一个元素,返回元素本身
  4. some():检查是否有元素满足条件,返回布尔值
let numbers = [10, 20, 30, 40, 50];

// includes - 简单值比较
console.log(numbers.includes(30)); // 输出: true

// indexOf - 返回索引
console.log(numbers.indexOf(30));  // 输出: 2
console.log(numbers.indexOf(60));  // 输出: -1(未找到)

// find - 使用条件查找
console.log(numbers.find(num => num > 25)); // 输出: 30(第一个满足条件的元素)

// some - 检查是否存在满足条件的元素
console.log(numbers.some(num => num > 45)); // 输出: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 7.7 实际应用场景

  1. 简单存在性检查:快速检查数组中是否存在某个值

    // 检查用户是否在允许列表中
    let allowedUsers = ["user1", "user2", "admin"];
    let currentUser = "user2";
    
    if (allowedUsers.includes(currentUser)) {
        console.log("用户允许访问");
    } else {
        console.log("用户无权访问");
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 多值检查:检查一个值是否在多个可能值中

    // 检查文件类型是否为图片
    let filename = "photo.jpg";
    let extension = filename.split('.').pop().toLowerCase();
    
    if (["jpg", "jpeg", "png", "gif"].includes(extension)) {
        console.log("这是一个图片文件");
    } else {
        console.log("这不是一个图片文件");
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  3. 避免重复添加:在添加元素前检查是否已存在

    // 添加标签,避免重复
    let tags = ["JavaScript", "编程", "Web开发"];
    let newTag = "JavaScript";
    
    if (!tags.includes(newTag)) {
        tags.push(newTag);
        console.log("标签已添加");
    } else {
        console.log("标签已存在,不需要添加");
    }
    
    console.log(tags); // 输出: ["JavaScript", "编程", "Web开发"](没有重复添加)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# 7.8 注意事项与兼容性

  1. NaN的处理:与大多数JavaScript比较不同,includes()可以正确找到NaN

    let numbers = [1, NaN, 3];
    console.log(numbers.includes(NaN)); // 输出: true
    
    1
    2
  2. 兼容性:includes()是ES6(ES2015)引入的方法,在旧浏览器中可能不支持

    // 在不支持includes的环境中的替代方案
    function includesPolyfill(array, item) {
        return array.indexOf(item) !== -1;
    }
    
    let numbers = [1, 2, 3];
    console.log(includesPolyfill(numbers, 2)); // 输出: true
    
    1
    2
    3
    4
    5
    6
    7
  3. 性能考虑:对于大型数组,如果需要频繁检查,考虑使用Set数据结构

    // 使用Set进行高效的存在性检查
    let largeArray = [/* 大量数据 */];
    let largeSet = new Set(largeArray);
    
    // 使用Set.has()比Array.includes()更高效
    console.log(largeSet.has(42)); // 比largeArray.includes(42)更快
    
    1
    2
    3
    4
    5
    6

小贴士

includes()方法是进行简单值检查的理想选择,特别是当你只需要知道"数组中是否包含某个值"而不关心它的位置或其他细节时。它的语法简单直观,使代码更易读。记住,它只适用于简单值的比较,对于对象或数组,它比较的是引用而非内容。


总结

以上数组遍历方法各有优势,应根据具体场景合理选择:

  • for循环适合简单、需要跳出循环的场景,性能较高。
  • forEach提升代码可读性,适合简洁场景,但不支持中途退出。
  • map、filter、reduce适用于复杂数据处理和生成新数组的场景,尤其是在对数据进行转换、筛选和汇总时。
  • every和some适用于数组元素的条件检查,分别对应"全部满足"和"至少一个满足"的场景。
  • includes适用于简单值的存在性检查,语法简洁直观。

# 8. 流式操作与终结方法

JavaScript数组提供了一系列方法,可以像流式处理一样链式调用,最后通过"终结方法"收集结果。这种编程模式类似于其他语言中的流式处理(Stream Processing)。

# 8.1 链式调用与流式处理

JavaScript允许我们将多个数组方法链接在一起,形成处理"管道":

// 一个包含学生信息的数组
let students = [
    { name: "张三", age: 16, score: 85 },
    { name: "李四", age: 17, score: 76 },
    { name: "王五", age: 18, score: 92 },
    { name: "赵六", age: 16, score: 88 },
    { name: "钱七", age: 17, score: 67 }
];

// 链式调用:筛选年龄大于等于17的学生,按分数排序,然后提取姓名
let result = students
    .filter(student => student.age >= 17)  // 筛选
    .sort((a, b) => b.score - a.score)     // 排序(降序)
    .map(student => student.name);         // 转换

console.log(result); // 输出: ["王五", "李四", "钱七"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在这个例子中,数据像流水一样依次通过filter、sort和map处理,最后得到结果。

# 8.2 终结操作方法

在JavaScript中,有些数组方法会"终结"链式调用并返回最终结果。这些方法通常被称为"终结操作":

# 8.2.1 reduce() 和 reduceRight()

这两个方法是最常见的终结操作,它们将数组归约为单个值:

// 使用reduce计算学生的平均分
let students = [
    { name: "张三", score: 85 },
    { name: "李四", score: 76 },
    { name: "王五", score: 92 }
];

let totalStudents = students.length;
let averageScore = students
    .map(student => student.score)         // 提取分数
    .reduce((sum, score) => sum + score, 0) / totalStudents;

console.log(`平均分: ${averageScore}`); // 输出: 平均分: 84.33...
1
2
3
4
5
6
7
8
9
10
11
12
13

reduceRight()与reduce()类似,但从数组的右侧(末尾)开始处理。

# 8.2.2 join()

将数组元素连接成字符串:

// 将筛选后的学生姓名连接成字符串
let topStudents = students
    .filter(student => student.score >= 85)
    .map(student => student.name)
    .join("、");

console.log(`优秀学生: ${topStudents}`); // 输出: 优秀学生: 张三、王五
1
2
3
4
5
6
7

# 8.2.3 find() 和 findIndex()

查找满足条件的第一个元素或其索引:

// 查找第一个90分以上的学生
let firstTopStudent = students
    .find(student => student.score > 90);

console.log(`第一个90分以上的学生: ${firstTopStudent?.name}`); // 输出: 第一个90分以上的学生: 王五
1
2
3
4
5

# 8.2.4 聚合方法:some()、every()、includes()

这些方法检查数组元素是否满足特定条件,返回布尔值:

// 检查是否所有学生都及格(60分以上)
let allPassed = students
    .every(student => student.score >= 60);

console.log(`所有学生都及格了吗? ${allPassed}`); // 输出: 所有学生都及格了吗? true
1
2
3
4
5

# 8.2.5 toArray() 方法(ES2023)

在较新的JavaScript版本中,可以使用Array.from()或展开运算符[...]将类数组对象或可迭代对象转换为数组:

// 将Map的键转换为数组
let studentScores = new Map([
    ["张三", 85],
    ["李四", 76],
    ["王五", 92]
]);

let studentNames = [...studentScores.keys()];
console.log(studentNames); // 输出: ["张三", "李四", "王五"]
1
2
3
4
5
6
7
8
9

# 8.3 实际应用:数据处理管道

流式操作在处理复杂数据时特别有用,可以构建清晰的数据处理管道:

// 一个包含商品订单的数组
let orders = [
    { id: 1, customer: "张三", items: ["手机", "充电器"], total: 6299, status: "已发货" },
    { id: 2, customer: "李四", items: ["笔记本电脑"], total: 8999, status: "待付款" },
    { id: 3, customer: "王五", items: ["耳机", "手表"], total: 2799, status: "已完成" },
    { id: 4, customer: "赵六", items: ["平板", "键盘", "鼠标"], total: 5899, status: "已发货" },
    { id: 5, customer: "钱七", items: ["智能音箱"], total: 599, status: "已完成" }
];

// 构建数据处理管道:
// 1. 筛选已完成的订单
// 2. 按总金额排序
// 3. 提取客户名和订单金额
// 4. 格式化为字符串列表
let completedOrderSummary = orders
    .filter(order => order.status === "已完成")
    .sort((a, b) => b.total - a.total)
    .map(order => `${order.customer}: ${order.total}元`)
    .join("\n");

console.log("已完成订单摘要:\n" + completedOrderSummary);
/* 输出:
已完成订单摘要:
王五: 2799元
钱七: 599元
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 8.4 性能考虑与惰性求值

JavaScript的数组方法是立即执行的(非惰性的),这意味着每个中间步骤都会创建一个新数组。对于大型数据集,这可能导致性能问题:

// 对于大型数组,这种链式调用可能效率较低
let result = bigArray
    .filter(x => x > 10)
    .map(x => x * 2)
    .reduce((sum, x) => sum + x, 0);
1
2
3
4
5

在处理大型数据集时,可以考虑以下优化方法:

  1. 合并操作:将多个操作合并到一个循环中

    // 更高效的方式
    let sum = 0;
    for (let x of bigArray) {
        if (x > 10) {
            sum += x * 2;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
  2. 使用第三方库:一些库(如Lodash)提供了惰性求值的集合操作

    // 使用Lodash的链式操作(惰性求值)
    const _ = require('lodash');
    let result = _(bigArray)
        .filter(x => x > 10)
        .map(x => x * 2)
        .sum();
    
    1
    2
    3
    4
    5
    6

小贴士

流式操作和终结方法使代码更简洁、更具声明性,让我们能够清晰地表达数据处理的意图。在大多数中小型数据处理场景中,这种方式既高效又易读。只有在处理非常大的数据集时,才需要考虑性能优化。

# 8.5 收集到其他数据结构

除了将数据收集到数组或单个值外,JavaScript还提供了多种方式将数据收集到其他数据结构中,如对象、Map、Set等。

# 8.5.1 收集到对象(Object)

使用reduce()方法可以将数组转换为对象:

// 将学生数组转换为以ID为键的对象
let students = [
    { id: 1, name: "张三", score: 85 },
    { id: 2, name: "李四", score: 76 },
    { id: 3, name: "王五", score: 92 }
];

// 转换为 {1: {学生对象}, 2: {学生对象}, ...} 的形式
let studentsById = students.reduce((obj, student) => {
    obj[student.id] = student; // 以学生ID为键,学生对象为值
    return obj;
}, {});

console.log(studentsById);
/* 输出:
{
  '1': { id: 1, name: '张三', score: 85 },
  '2': { id: 2, name: '李四', score: 76 },
  '3': { id: 3, name: '王五', score: 92 }
}
*/

// 可以快速查找特定ID的学生
console.log(studentsById[2]); // 输出: { id: 2, name: '李四', score: 76 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

也可以使用Object.fromEntries()方法(ES2019引入)将键值对数组转换为对象:

// 使用map生成键值对,然后转换为对象
let nameScoreMap = Object.fromEntries(
    students.map(student => [student.name, student.score])
);

console.log(nameScoreMap);
/* 输出:
{
  '张三': 85,
  '李四': 76,
  '王五': 92
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.5.2 收集到Map

Map是ES6引入的键值对集合,比普通对象更灵活,可以使用任何类型的值作为键:

// 将学生数组转换为Map(以学生对象为键,分数为值)
let studentScoreMap = new Map(
    students.map(student => [student, student.score])
);

// 使用对象作为键(普通对象做不到这一点)
let zhang = students[0];
console.log(studentScoreMap.get(zhang)); // 输出: 85

// 也可以使用reduce方法构建Map
let nameToStudentMap = students.reduce((map, student) => {
    return map.set(student.name, student); // 使用set方法添加键值对
}, new Map());

console.log(nameToStudentMap.get("李四")); // 输出: { id: 2, name: '李四', score: 76 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 8.5.3 收集到Set

Set是ES6引入的值的集合,其中的每个值都是唯一的:

// 从数组中提取所有不同的分数到Set中
let scores = [85, 76, 92, 85, 76, 88, 92, 95];
let uniqueScores = new Set(scores);

console.log(uniqueScores); // 输出: Set { 85, 76, 92, 88, 95 }

// 使用数组方法生成Set
let passingScores = new Set(
    scores.filter(score => score >= 80)
);

console.log(passingScores); // 输出: Set { 85, 92, 88, 95 }

// 从对象数组中提取特定属性的唯一值
let courses = [
    { id: 1, category: "编程", name: "JavaScript基础" },
    { id: 2, category: "编程", name: "Python入门" },
    { id: 3, category: "设计", name: "UI设计原则" },
    { id: 4, category: "编程", name: "Java编程" }
];

let categories = new Set(
    courses.map(course => course.category)
);

console.log(categories); // 输出: Set { '编程', '设计' }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 8.5.4 分组收集(Group By)

ES2023引入了Object.groupBy()方法,可以方便地将数组元素按照特定条件分组:

// 注意:这是一个较新的特性,可能需要使用最新的Node.js或浏览器
// 按照分数等级对学生分组
let students = [
    { name: "张三", score: 85 },
    { name: "李四", score: 76 },
    { name: "王五", score: 92 },
    { name: "赵六", score: 65 }
];

// 使用Object.groupBy (ES2023)
const getGrade = (student) => {
    if (student.score >= 90) return 'A';
    if (student.score >= 80) return 'B';
    if (student.score >= 70) return 'C';
    if (student.score >= 60) return 'D';
    return 'F';
};

// 如果浏览器支持Object.groupBy
// let studentsByGrade = Object.groupBy(students, getGrade);

// 如果不支持,可以使用reduce实现相同功能
let studentsByGrade = students.reduce((groups, student) => {
    const grade = getGrade(student);
    if (!groups[grade]) {
        groups[grade] = [];
    }
    groups[grade].push(student);
    return groups;
}, {});

console.log(studentsByGrade);
/* 输出:
{
  'B': [{ name: '张三', score: 85 }],
  'C': [{ name: '李四', score: 76 }],
  'A': [{ name: '王五', score: 92 }],
  'D': [{ name: '赵六', score: 65 }]
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 8.5.5 收集到字符串

除了join()方法外,还可以使用模板字符串和reduce()方法生成更复杂的字符串:

// 生成HTML列表
let fruits = ["苹果", "香蕉", "橙子"];

// 使用join和map
let htmlList1 = `<ul>
  ${fruits.map(fruit => `<li>${fruit}</li>`).join('')}
</ul>`;

// 使用reduce
let htmlList2 = fruits.reduce((html, fruit) => {
    return html + `  <li>${fruit}</li>\n`;
}, '<ul>\n') + '</ul>';

console.log(htmlList2);
/* 输出:
<ul>
  <li>苹果</li>
  <li>香蕉</li>
  <li>橙子</li>
</ul>
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 8.5.6 实际应用:数据转换管道

在实际开发中,我们经常需要将数据从一种结构转换为另一种结构,以下是一个综合示例:

// 假设这是从API获取的原始数据
let apiResponse = [
    { id: 101, name: "产品A", price: 99.99, categories: ["电子", "配件"] },
    { id: 102, name: "产品B", price: 149.99, categories: ["电子", "家电"] },
    { id: 103, name: "产品C", price: 29.99, categories: ["配件"] },
    { id: 104, name: "产品D", price: 599.99, categories: ["电子", "家电"] }
];

// 1. 提取所有唯一的产品类别
let allCategories = new Set(
    apiResponse.flatMap(product => product.categories)
);
console.log("所有产品类别:", allCategories);

// 2. 按类别分组产品
let productsByCategory = {};
apiResponse.forEach(product => {
    product.categories.forEach(category => {
        if (!productsByCategory[category]) {
            productsByCategory[category] = [];
        }
        productsByCategory[category].push({
            id: product.id,
            name: product.name,
            price: product.price
        });
    });
});
console.log("按类别分组的产品:", productsByCategory);

// 3. 创建产品ID到产品的映射
let productsById = new Map(
    apiResponse.map(product => [product.id, product])
);
console.log("通过ID查找产品:", productsById.get(102));

// 4. 生成价格区间统计
let priceRanges = {
    "低价(0-100)": 0,
    "中价(100-300)": 0,
    "高价(300+)": 0
};

apiResponse.forEach(product => {
    if (product.price < 100) {
        priceRanges["低价(0-100)"]++;
    } else if (product.price < 300) {
        priceRanges["中价(100-300)"]++;
    } else {
        priceRanges["高价(300+)"]++;
    }
});
console.log("价格区间统计:", priceRanges);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

小贴士

选择合适的数据结构对于提高代码效率和可读性至关重要。在处理复杂数据时,考虑最终需要如何使用这些数据,然后选择最适合的收集方式:

  • 使用对象当你需要通过字符串或数字键快速查找值
  • 使用Map当你需要使用非字符串键或需要保持插入顺序
  • 使用Set当你只关心唯一值的集合
  • 使用数组当你需要保持元素顺序或进行进一步的数组操作
编辑此页 (opens new window)
上次更新: 2025/03/16, 13:51:03
数组
函数

← 数组 函数→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式