JavaScript 高阶函数

382 阅读4分钟

写在前面

还在为你的js代码不够优雅犯愁么?
学习JavaScript高阶函数实现你的奇技淫巧,让别人看不懂你的代码。

JavaScript 原生高阶函数

高阶函数就是一个将函数作为参数或者返回值的函数。下面我们一起来学习一下 map reduce filter

Array.map

语法:

 var new_array = arr.map(function callback(currentValue[, index[, array]]) {
     // Return element for new_array 
    }[, thisArg])

参数解释:

currentValue 数组中正在处理的当前元素。
index(可选) 数组中正在处理的当前元素的索引。
array(可选) map 方法调用的数组。
thisArg(可选) 执行 callback 函数时值被用作this。

简单解释:

按照传入的函数处理数组中的每一个元素,并返回一个相同长度的新数组。

案例分享:

// 实现函数f(x)=x^2的功能
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

arr.map( item => item * item )

//结果为: [1, 4, 9, 16, 25, 36, 49, 64, 81]

Array.reduce

语法:

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

参数解释:

accumulator 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue。
currentValue 数组中正在处理的元素。
index(可选) 数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
array(可选) 调用reduce()的数组
initialValue(可选) 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

简单解释:

数组中的元素挨个执行函数并且执行后的返回值会被记住并传给下一次调用

案例分享:

// 把[1, 3, 5, 7, 9] 变换成整数 13579
var arr = [1, 3, 5, 7, 9];

arr.reduce((result, item)=> ( result = result*10 + item ))

//结果为: 13579


// 一个字符串中每个字母出现的次数

var str = "aabbcddeffffa"
str.split("").reduce((result, item) => {result[item] = result[item] ? result[item] + 1 : 1; return result }, {})

// {a: 3, b: 2, c: 1, d: 2, e: 1, f: 4}

str.split("").reduce((result, item) => ({[item]: result[item]? result[item]++: 1,...result }),{})

// {a: 3, b: 2, c: 1, d: 2, e: 1, f: 4}


// 需要将数组中的对象转变成 {1: "apple", 2: "banana", 3: "orange"}
var arr = [
    { id: 1,name: "apple" },
    { id: 2, name: "banana"},
    { id: 3, name: "orange"},
    ]

arr.reduce((obj, item) => ({
    ...obj,
    [item.id]: item.name
}), {})

// {1: "apple", 2: "banana", 3: "orange"}

Array.filter

语法:

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

参数解释:

element 数组中当前正在处理的元素。
index(可选) 正在处理的元素在数组中的索引。
array(可选) 调用了 filter 的数组本身。
thisArg(可选) 执行 callback 时,用于 this 的值。

简单解释:

filter的作用是过滤掉数组中的某些元素,然后返回剩下的元素

案例分享:

// 筛选出基数
var arr = [1, 7, 4, 2, 6, 9, 10, 11];
arr.filter(function (x) {
    return x % 2 !== 0;
});
//结果为: [1, 7, 9, 11]

综合使用

下例 实现对数据筛选 处理 求和的一个过程;让我们感受一下鬼斧神工


// 这是一个比较复杂的例子,目的实现对数据筛选 处理 求和的一个过程 感受一下魅力


// 初始数据的定义
var personnel = [
  {
    id: 5,
    name: "Luke Skywalker",
    pilotingScore: 98,
    shootingScore: 56,
    isForceUser: true,
  },
  {
    id: 82,
    name: "Sabine Wren",
    pilotingScore: 73,
    shootingScore: 99,
    isForceUser: false,
  },
  {
    id: 22,
    name: "Zeb Orellios",
    pilotingScore: 20,
    shootingScore: 59,
    isForceUser: false,
  },
  {
    id: 15,
    name: "Ezra Bridger",
    pilotingScore: 43,
    shootingScore: 67,
    isForceUser: true,
  },
  {
    id: 11,
    name: "Caleb Dume",
    pilotingScore: 71,
    shootingScore: 85,
    isForceUser: true,
  },
];

// 筛选出 isForceUser = true 的数据
jediPersonnel = personnel.filter(function (person) {
  return person.isForceUser;
});
// Result: [{...}, {...}, {...}] (Luke, Ezra and Caleb)

// 计算出数组中各元素 驾驶得分与射击得分的和 
var jediScores = jediPersonnel.map(function (jedi) {
  return jedi.pilotingScore + jedi.shootingScore;
});
// Result: [154, 110, 156]

// 最终计算出所有得分的总和
var totalJediScore = jediScores.reduce(function (acc, score) {
  return acc + score;
}, 0);
// Result: 420


// 合并代码
totalJediScore = personnel
  .filter(person => person.isForceUser)
  .map(jedi => jedi.pilotingScore + jedi.shootingScore)
  .reduce((acc, score) => acc + score, 0);

总结

  • 在其他语言中也有类似的高阶函数本质上的功能都类似 如果读懂此文那么其他语言的map/reduce/filter也是信手拈来
  • 虽说一般的循环判断也是可以实现类似功能,但是写代码总是要有追求的。多看多学习别人的代码,你就能写出越来越简洁,越来越优雅的代码来。
  • 可能对于初学者来说不是很好理解其中的逻辑,但是如果大家建立在理解map/reduce/filter基础之上反而能够更清晰的了解到其中的逻辑。

参考资料

JavaScript 标准内置对象

[译] 图解 Map、Reduce 和 Filter 数组方法

An Illustrated (and Musical) Guide to Map, Reduce, and Filter Array Methods