JavaScript数组高阶函数之filter、map与reduce详解
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">1. 函数式编程简介</a></li><li><a href="#_label1">2. JavaScript数组迭代基础</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">2.1 传统迭代方法</a></li><li><a href="#_lab2_1_1">2.2 高阶函数的优势</a></li></ul><li><a href="#_label2">3. filter 函数:数组过滤</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_2">基本概念</a></li><li><a href="#_lab2_2_3">语法结构</a></li><li><a href="#_lab2_2_4">核心特性</a></li><li><a href="#_lab2_2_5">使用示例</a></li><li><a href="#_lab2_2_6">箭头函数简化</a></li></ul><li><a href="#_label3">4. map 函数:数组映射</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_7">基本概念</a></li><li><a href="#_lab2_3_8">语法结构</a></li><li><a href="#_lab2_3_9">核心特性</a></li><li><a href="#_lab2_3_10">使用示例</a></li><li><a href="#_lab2_3_11">箭头函数简化</a></li></ul><li><a href="#_label4">5. reduce 函数:数组汇总</a></li><ul class="second_class_ul"><li><a href="#_lab2_4_12">基本概念</a></li><li><a href="#_lab2_4_13">语法结构</a></li><li><a href="#_lab2_4_14">核心特性</a></li><li><a href="#_lab2_4_15">使用示例</a></li><li><a href="#_lab2_4_16">箭头函数简化</a></li></ul><li><a href="#_label5">6. 链式调用:组合使用</a></li><ul class="second_class_ul"><li><a href="#_lab2_5_17">完整示例</a></li><li><a href="#_lab2_5_18">复杂数据处理示例</a></li></ul><li><a href="#_label6">7. 三种方法对比总结</a></li><ul class="second_class_ul"></ul><li><a href="#_label7">8. 性能考虑与优化建议</a></li><ul class="second_class_ul"></ul><li><a href="#_label8">9. 实际应用场景</a></li><ul class="second_class_ul"><li><a href="#_lab2_8_19">数据处理管道</a></li><li><a href="#_lab2_8_20">数组去重</a></li></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>1. 函数式编程简介</h2><p>在JavaScript编程中,数组迭代是一项基础而重要的任务。随着函数式编程范式的普及,JavaScript提供了一系列强大的数组高阶函数,其中<strong>map、filter和reduce</strong>是最为核心和常用的三个方法。与传统的命令式编程(如for循环)相比,这些高阶函数采用<strong>声明式编程</strong>风格,使代码更加简洁、可读和易于维护。</p>
<p>函数式编程的核心思想是<strong>将函数作为第一公民</strong>,即函数可以像其他数据类型一样被传递、赋值和返回。这种编程范式与面向对象编程形成鲜明对比,它更注重<strong>数据的转换</strong>而非状态的改变。在JavaScript中,数组高阶函数正是这种思想的完美体现。</p>
<blockquote><p>函数式编程是一种以函数为第一公民的编程范式,与命令式编程和面向对象编程形成对比。在 JavaScript 中,数组的高阶函数 filter、map 和 reduce 是函数式编程的典型代表,它们可</p>
<p>以使代码更加简洁、易读和可维护。</p></blockquote>
<p class="maodian"><a name="_label1"></a></p><h2>2. JavaScript数组迭代基础</h2>
<p>在深入探讨高阶函数之前,我们先简要回顾JavaScript中数组迭代的基础方法。</p>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>2.1 传统迭代方法</h3>
<p><strong>for循环</strong>是最基础的迭代方法,通过索引访问数组的每个元素:</p>
<div class="jb51code"><pre class="brush:js;">let array = ;
for (let i = 0; i < array.length; i++) {
console.log(array); // 输出数组中的每个元素
}</pre></div>
<p><strong>forEach方法</strong>是ES5引入的更简洁的遍历方式:</p>
<div class="jb51code"><pre class="brush:js;">let array = ;
array.forEach(function(item) {
console.log(item); // 输出数组中的每个元素
});</pre></div>
<p><strong>for...of循环</strong>是ES6引入的语法,直接迭代数组的元素:</p>
<div class="jb51code"><pre class="brush:js;">let array = ;
for (let item of array) {
console.log(item); // 输出数组中的每个元素
}</pre></div>
<p>虽然这些方法都能实现数组迭代,但它们各有优缺点。for循环灵活但代码冗长;forEach简洁但无法中断遍历;for...of语法简洁但兼容性需要考虑。</p>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>2.2 高阶函数的优势</h3>
<p>与上述方法相比,map、filter和reduce等高阶函数具有以下显著优势:</p>
<ol><li><p><strong>声明式编程</strong>:关注"做什么"而非"怎么做",代码更易理解</p></li><li><p><strong>不可变性</strong>:不会修改原数组,而是返回新数组,符合函数式编程原则</p></li><li><p><strong>链式调用</strong>:可以轻松组合多个操作,形成清晰的数据处理管道</p></li><li><p><strong>代码复用</strong>:将通用逻辑抽象为函数,提高代码的可维护性</p></li></ol>
<p class="maodian"><a name="_label2"></a></p><h2>3. filter 函数:数组过滤</h2>
<p class="maodian"><a name="_lab2_2_2"></a></p><p class="maodian"><a name="_lab2_3_7"></a></p><p class="maodian"><a name="_lab2_4_12"></a></p><h3>基本概念</h3>
<p>filter 函数用于创建一个新数组,包含通过指定函数测试的所有元素。它不会改变原始数组,而是返回一个新的过滤后的数组。</p>
<p class="maodian"><a name="_lab2_2_3"></a></p><p class="maodian"><a name="_lab2_3_8"></a></p><p class="maodian"><a name="_lab2_4_13"></a></p><h3>语法结构</h3>
<div class="jb51code"><pre class="brush:js;">let newArray = arr.filter(function(currentValue[, index[, array]]) {
// 返回 true 保留元素,false 过滤元素
}[, thisArg])</pre></div>
<p class="maodian"><a name="_lab2_2_4"></a></p><p class="maodian"><a name="_lab2_3_9"></a></p><p class="maodian"><a name="_lab2_4_14"></a></p><h3>核心特性</h3>
<ul><li><p><strong>过滤机制</strong>:根据回调函数返回的布尔值决定是否保留元素(<code>true</code>保留,<code>false</code>过滤)</p></li><li><p><strong>应用场景</strong>:从数组中筛选出满足特定条件的元素</p></li></ul>
<p class="maodian"><a name="_lab2_2_5"></a></p><p class="maodian"><a name="_lab2_3_10"></a></p><p class="maodian"><a name="_lab2_4_15"></a></p><h3>使用示例</h3>
<div class="jb51code"><pre class="brush:js;">const nums =
let newNums = nums.filter(function(n) {
return n < 100
})
// 结果: </pre></div>
<p class="maodian"><a name="_lab2_2_6"></a></p><p class="maodian"><a name="_lab2_3_11"></a></p><p class="maodian"><a name="_lab2_4_16"></a></p><h3>箭头函数简化</h3>
<div class="jb51code"><pre class="brush:js;">let newNums = nums.filter(n => n < 100)</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>4. map 函数:数组映射</h2>
<h3>基本概念</h3>
<p>map 函数创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后的返回值。它不会改变原数组,而是返回一个新数组。</p>
<h3>语法结构</h3>
<div class="jb51code"><pre class="brush:js;">let newArray = arr.map(function(currentValue[, index[, array]]) {
// 返回新元素值
}[, thisArg])</pre></div>
<h3>核心特性</h3>
<ul><li><p><strong>映射转换</strong>:对数组中的每个元素应用转换函数,生成新数组</p></li><li><p><strong>应用场景</strong>:将数组元素从一种形式转换为另一种形式</p></li></ul>
<h3>使用示例</h3>
<div class="jb51code"><pre class="brush:js;">let new2Nums = newNums.map(function(n) {
return n * 2
})
// 结果: </pre></div>
<h3>箭头函数简化</h3>
<div class="jb51code"><pre class="brush:js;">let new2Nums = newNums.map(n => n * 2)</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>5. reduce 函数:数组汇总</h2>
<h3>基本概念</h3>
<p>reduce 函数对数组中的每个元素执行一个由您提供的 reducer 函数,将其结果汇总为单个返回值。它是处理数组汇总操作的强大工具。</p>
<h3>语法结构</h3>
<div class="jb51code"><pre class="brush:js;">let result = arr.reduce(function(accumulator, currentValue[, index[, array]]) {
// 返回新的累加器值
}[, initialValue])</pre></div>
<h3>核心特性</h3>
<ul><li><p><strong>汇总聚合</strong>:将数组中的所有元素通过回调函数累加为一个最终值</p></li><li><p><strong>参数说明</strong>:</p>
<ul><li><p><code>accumulator</code>:累加器,保存上一次回调的返回值</p></li><li><p><code>currentValue</code>:当前正在处理的元素</p></li><li><p><code>initialValue</code>(可选):作为第一次调用回调函数的初始值</p></li></ul></li></ul>
<h3>使用示例</h3>
<div class="jb51code"><pre class="brush:js;">let total = new2Nums.reduce(function(preValue, n) {
return preValue + n
}, 0)
// 结果: 240</pre></div>
<h3>箭头函数简化</h3>
<div class="jb51code"><pre class="brush:js;">let total = new2Nums.reduce((pre, n) => pre + n, 0)</pre></div>
<p class="maodian"><a name="_label5"></a></p><h2>6. 链式调用:组合使用</h2>
<p>这三个方法的真正威力在于可以将它们链式调用,创建出高效且表达性强的数据处理流程。</p>
<p><strong>链式调用的开销</strong>:每个方法都会生成新数组,处理大规模数据时可能影响性能。此时可考虑使用 <code>for</code>循环合并操作。</p>
<p class="maodian"><a name="_lab2_5_17"></a></p><h3>完整示例</h3>
<div class="jb51code"><pre class="brush:js;">// 分开调用
let total = nums.filter(n => n < 100)
.map(n => n * 2)
.reduce((pre, n) => pre + n, 0)
// 单行简洁写法
let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n)</pre></div>
<p class="maodian"><a name="_lab2_5_18"></a></p><h3>复杂数据处理示例</h3>
<div class="jb51code"><pre class="brush:js;">// 处理对象数组的复杂案例
let users = [
{ name: 'Alice', age: 22 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 }
]
let totalAge = users.filter(user => user.age > 18)
.map(user => user.age)
.reduce((sum, age) => sum + age, 0)
// 结果: 52</pre></div>
<p class="maodian"><a name="_label6"></a></p><h2>7. 三种方法对比总结</h2>
<table><thead><tr><th><p>方法</p></th><th><p>返回类型</p></th><th><p>是否修改原数组</p></th><th><p>主要用途</p></th></tr></thead><tbody><tr><td><p>filter</p></td><td><p>新数组</p></td><td><p>否</p></td><td><p>过滤数组中符合条件的元素</p></td></tr><tr><td><p>map</p></td><td><p>新数组</p></td><td><p>否</p></td><td><p>转换数组中的每个元素</p></td></tr><tr><td><p>reduce</p></td><td><p>任意类型(单个值)</p></td><td><p>否</p></td><td><p>将数组聚合为一个值</p></td></tr></tbody></table>
<p class="maodian"><a name="_label7"></a></p><h2>8. 性能考虑与优化建议</h2>
<p>虽然这些高阶函数提供了声明式和简洁的数组处理方式,但在处理大型数据集时,它们的性能可能不如传统的 for 循环。这是因为高阶函数在内部会进行循环迭代,但额外的函数调用和上下文切换可能会引入额外开销。</p>
<p><strong>优化建议</strong>:</p>
<ul><li><p>避免不必要的链式调用,尽量在一个迭代中完成多个操作</p></li><li><p>处理大型数据集时,考虑使用传统的 for 循环</p></li><li><p>链式调用时,先使用 filter 方法减少数据集,再进行 map 和 reduce 操作</p></li></ul>
<p class="maodian"><a name="_label8"></a></p><h2>9. 实际应用场景</h2>
<p class="maodian"><a name="_lab2_8_19"></a></p><h3>数据处理管道</h3>
<div class="jb51code"><pre class="brush:js;">let data =
let result = data.filter(num => num % 2 === 0) // 筛选偶数
.map(num => num * 2) // 乘以2
.filter(num => num > 5) // 筛选大于5的数
.reduce((sum, num) => sum + num, 0) // 求和
// 结果: 24</pre></div>
<p class="maodian"><a name="_lab2_8_20"></a></p><h3>数组去重</h3>
<div class="jb51code"><pre class="brush:js;">let arr =
let uniqueArr = arr.filter((value, index, self) => {
return self.indexOf(value) === index
})
// 结果: </pre></div>
<p>这些数组高阶函数是现代 JavaScript 开发中的重要工具,掌握它们能够帮助你写出更简洁、优雅和可维护的代码。</p>
頁:
[1]