JavaScript 高级函数 - React 04

Javascript 高阶函数

序言

本文将介绍一个非常重要的 JavaScript 函数概念--高阶函数。

高阶函数是将一个或多个函数作为参数返回,或将一个函数作为结果返回的函数。

本文将详细介绍什么是高阶函数、使用高阶函数的好处以及如何在实际应用中使用高阶函数。

  1. 功能定位是什么?
  2. 纯功能
  3. 高阶函数
  4. 柯理化

功能定位是什么?

所谓函数定向,简而言之,就是函数本身就是一个变量。例如,可以使用 const、let 或 var 声明函数,并将其转化为参数传递给其他函数,或添加到数组或对象中。

将一个函数作为引号传递给另一个函数。

				
					const print = (message) => { console.log(`print function with ${message}`) } const helloMessage = () => { return "Hello Message" } print(helloMessage()); // print function with Hello Message
				
			

添加数组

				
					const array = ["item0", (message) => console.log("I'm item function in array " + message)] console.log(array[0]); // item0 array[1]("argument "); // I'm item function in array argument
				
			

添加对象

				
					const object = { helloWorld: "Hello World", print: (message) => { console.log(`print function with ${message}`) } } object.print(object.helloWorld); // print function with Hello World
				
			

纯功能

当一个函数只受一个参数影响时,它被称为纯函数。这样的纯函数,因为不会受到其他变量的干扰,所以这样的函数是密封的,不会受到其他变量和引号的干扰,即会产生副作用。

所谓 "副作用"(Side Effect)是指在函数执行过程中发生的外部变化。

  1. 使用 Date.now() 或 Math.random()
  2. 使用 console.log()
  3. 修改外部信息
  4. 操作 DOM
  5. 提出 HTTP 请求

让我们看看下面这个例子,它是一个非纯函数,在修改外部数据时会受到影响。

				
					let y = 1; function xAdd(x) { return x + y; }; xAdd(5); //6
				
			

可以看到,即使执行了 xAdd(5),结果也会因为 y 的变化而不同。

因此,我们应该将函数变成一个纯函数,它是封装的,不受外部影响。纯函数的优势不仅在于它的独立性,还在于它更容易编写测试。

				
					function sum(x, y) { return x + y; }; sum(1,2); //3
				
			

高阶函数

高阶函数是 "接受或返回一个函数 "的函数。

接受是指将函数作为参数输入。

回调函数将函数作为变量返回,有几种不同类型的高阶函数,如 map 和 reduce。

上面的功能指南就是其中的一个例子,让我们来回顾一下。

				
					// Callback function, passed as a parameter in the higher order function function callbackFunction(){ console.log('I am a callback function'); } // higher order function function higherOrderFunction(func){ console.log('I am higher order function') func() } higherOrderFunction(callbackFunction);
				
			

在上述代码中,higherOrderFunction() 是一个 HOF,因为我们将一个回调函数作为参数传给了它。

上面的例子很简单,让我们进一步看看如何使用 HOF 来编写更简洁、更模块化的代码。

高阶函数的工作原理

假设您想编写一个计算圆的面积和直径的函数。作为编程新手,首先想到的解决方案是分别编写计算圆的面积或直径的函数。

				
					const radius = [1, 2, 3]; // function to calculate area of the circle const calculateArea = function (radius) { const output = []; for(let i = 0; i< radius.length; i++){ output.push(Math.PI * radius[i] * radius[i]); } return output; } // function to calculate diameter of the circle const calculateDiameter = function (radius) { const output = []; for(let i = 0; i< radius.length; i++){ output.push(2 * radius[i]); } return output; } console.log(calculateArea(radius)); console.log(calculateDiameter(radius))
				
			

但你注意到上述代码的问题了吗?

我们在重复编写几乎相同的函数,但逻辑略有不同,而且我们编写的函数不能重复使用,所以让我们看看如何使用高阶函数来编写相同的代码:

				
					const radius = [1, 2, 3]; // logic to clculate area const area = function(radius){ return Math.PI * radius * radius; } // logic to calculate diameter const diameter = function(radius){ return 2 * radius; } // a reusable function to calculate area, diameter, etc const calculate = function(radius, logic){ const output = []; for(let i = 0; i < radius.length; i++){ output .push(logic(radius[i])) } return output; } console.log(calculate(radius, area)); console.log(calculate(radius, diameter));
				
			

正如您在上面的代码中看到的,我们只编写了一个函数 calculate() 来计算圆的面积和直径。我们只需编写逻辑并将其传递给 calculate(),函数就会完成它的工作。

我们使用高级函数编写的代码简洁而模块化,每个函数各司其职,我们不必在此重复任何内容。

假设将来我们要编写一个计算圆周长的程序。我们需要做的就是编写计算圆周率的逻辑,并将其传递给 calculate() 函数。

				
					const circumeference = function(radius){ return 2 * Math.PI * radius; } console.log(calculate(radius, circumeference));
				
			

这里还提供了箭头函数的其他反例和应用。

				
					const print = (message) => { console.log(`print function with ${message}`) } const helloMessage = () => { return "Hello Message" } print(helloMessage()); // print function with Hello Message
				
			

不过,高阶函数可以让我们处理更方便、更复杂的情况。

				
					const printNameByCondition = (condition, trueFunc, falseFunc) => { condition ? trueFunc() : falseFunc(); } const printHogan = () => console.log("Hello Hogan"); const printBobo = () => console. log("Hello BoBo"); printNameByCondition(true, printHogan, printBobo); // Hello Hogan printNameByCondition(false, printHogan, printBobo); // Hello BoBo
				
			

如图所示,我创建了一个有三个参数的函数,其中最后两个是函数。

通过第一个引号,做出判断,如果为真,则执行第一个函数,否则执行第二个函数。

如何使用高阶函数

我们可以通过多种方式使用高阶函数。在处理数组时,我们可以使用 map()、reduce()、filter() 和 sort() 函数来操作和转换数组中的数据。

HOF 处理对象

您可以使用 Object.entries() 函数从一个对象创建一个新对象。

HOF 使用功能

您可以使用 compose() 函数从较简单的函数创建复杂的函数。

如何使用一些重要的高阶函数

JavaScript 内置了许多高阶函数,其中最常见的是 map()、filter() 和 reduce()。让我们来详细了解一下它们。

如何在 JavaScript 中使用 map()

map() 函数接收一个数组,并在不改变原始数组的情况下转换数组中的每个值。它通常用于将一个数组的值转换成一个具有不同结构的新数组。

示例 1:假设我们要给数组中的每个元素添加 10.我们可以使用 map() 方法将数组中的每个元素添加到 10

				
					const arr = [1, 2, 3, 4, 5]; const output = arr.map((num) => num += 10) console.log(arr); // [1, 2, 3, 4, 5] console.log(output); // [11, 12, 13, 14, 15]
				
			

在上例中,arr 是一个包含五个元素的数组:

我们使用 map 方法对数组中的每个元素应用一个函数,然后发回一个包含修改后元素的新数组。

传递给地图的回调函数使用了一个包含参数 num 的箭头函数。

该函数将 10 加到 num(数组的每个元素),并返回结果。

例 2:下面是一个用户数组。假设我们只需要用户的姓名。我们只需使用 map() 方法从用户数组中提取数据。

				
					const users = [ {firstName: 'John', lastName: 'Doe', age: 25}, {firstName: 'Jane', lastName: 'Doe', age: 30}, {firstName: 'Jack', lastName: ' Doe', age: 35}, {firstName: 'Jill', lastName: 'Doe', age: 40}, {firstName: 'Joe', lastName: 'Doe', age: 45}, ] const result = users. map((user) => user.firstName + ' ' + user.lastName) console.log(result); // ['John Doe', 'Jane Doe', 'Jack Doe', 'Jill Doe', 'Joe Doe']
				
			

在上面的代码中,users 是一个代表用户的对象数组。每个对象有三个属性:姓、名和年龄。

我们使用 map() 方法对每个用户进行映射,以获取 name 和 lastName 属性。

回调函数接收用户参数,该参数代表用户数组(对象)中的一个元素。

该函数串联用户的 name 和 lastName 属性并返回结果。

如何在 JavaScript 中使用 filter()

filter() 函数接收一个数组,并返回一个新数组,其中只包含符合特定条件的值。

它也不会改变原始数组,通常用于根据特定条件从数组中选择数据集。

Example 1: Use the filter() function to return a legend from an array of numbers.

				
					const arr = [1, 2, 3, 4, 5]; const output = arr.filter((num) => num % 2) // filter out odd numbers console.log(arr); // [1, 2 , 3, 4, 5] console.log(output); // [1, 3, 5]
				
			

在上述代码中,arr 是一个包含五个元素的数组:

filter 是一种创建新数组的方法,其元素必须通过所提供的回调函数中指定的测试。

回调函数通过检查 num 是否能被 2 整除(num % 2)来检查 num 是否为奇数。如果 num 不能被 2 整除,函数返回 true,否则返回 false。

在数组上使用 filter 时,函数会应用于数组的每个元素,创建一个新数组,其中只包含返回 true 或通过指定条件的元素。原始数组保持不变,并返回结果。

例 2:可以使用 filter() 只返回数组中年龄超过 30 岁的用户。

				
					const users = [ {firstName: 'John', lastName: 'Doe', age: 25}, {firstName: 'Jane', lastName: 'Doe', age: 30}, {firstName: 'Jack', lastName: ' Doe', age: 35}, {firstName: 'Jill', lastName: 'Doe', age: 40}, {firstName: 'Joe', lastName: 'Doe', age: 45}, ] // Find the users with age greater than 30 const output = users.filter(({age}) => age > 30) console.log(output); // [{firstName: 'Jack', lastName: 'Doe', age: 35} , {firstName: 'Jill', lastName: 'Doe', age: 40}, {firstName: 'Joe', lastName: 'Doe', age: 45}]
				
			

在上述代码中,users 是一个用户数组。每个对象都有三个属性:名、姓和年龄。

对用户数组使用过滤器,并对数组中的每个元素使用回调函数。

函数需要一个参数,换句话说,就是一个重新组合成单一属性年龄的对象。

该函数检查年龄是否大于 30 岁。如果是,函数返回 true,否则返回返回 false。

在用户上使用过滤器时,它会在数组的每个元素上使用该函数,创建一个新数组,该数组只包含通过该函数的元素。返回 true 的元素,並返回结果原用户数组保持不变。

如何在 JavaScript 中使用 reduce()

reduce() 在很多人的心目中是一个相对复杂的函数,如果你以前遇到过 reduce() 方法,但一开始弄不明白,请继续往下看!

我们可能会有一个问题:为什么要使用 reduce()?既然已经有很多好的函数,我们如何决定使用哪个函数,以及何时使用?

对于 reduce(),应在需要对数组元素执行操作并返回单一值时使用。

单值:对数组元素重复应用函数的累积结果。

例如,您可以使用 reduce() 求数组中所有元素的和、查找最大值或最小值、将多个对象合并为一个对象,或拆分数组中的不同元素。下面我们来举例说明。

例 1:使用 reduce() 求数组中所有元素的和:

				
					const numbers = [1, 2, 3, 4, 5]; const sum = numbers.reduce((total, currentValue) => { return total + currentValue; }, 0) console.log(sum); // 15
				
			

在本例中,reduce() 方法用于一个数组,并传递一个带有两个参数的回调函数:total 和 currentValue。

total 参数是使用该函数时返回值的总和,currentValue 是正在处理的数组的当前元素。

reduce() 的第二个参数也是初始值,在上述示例中为 0,用作第一个堆栈总计的初始值。

在每一次叠加中,函数都会将当前值与总值相加,并将当前值与总值相加。返回总数的新值。

接下来,reduce() 方法将返回的值作为下一次迭代的总值,直到数组中的所有元素都处理完毕。

最后,它会返回总数的最终值,即数组中所有元素的总和。

Example 2: Use reduce() to find the maximum value in a column:

				
					let numbers = [5, 20, 100, 60, 1]; const maxValue = numbers.reduce((max, curr) => { if(curr > max) max = curr; return max; }); console.log( maxValue); // 100
				
			

在本例中,我们再次在回调函数中使用了 max 和 curr 参数,这次没有在 reduce() 方法中传递第二个参数。因此,默认值将是数组中的第一个元素。

回调函数首先检查当前元素 curr 是否大于当前最大值 max;如果是,则更新 max 值,使其成为当前元素;如果不是,则不更新 max。如果不是,则不更新 max,最后返回 max 的值。

在本例中,reduce() 方法首先将 max 设置为 5,将 curr 设置为 20,然后检查 20 是否大于 5,如果大于 5,则将 max 更新为 20。

然后将 curr 设置为 100,检查 100 是否大于 20,结果是大于 20,因此将 max 更新为 100。

这个过程一直持续到处理完数组中的所有元素为止。 max 的最终值将是数列中的最大值,在此范例中就是100。

高阶函数的优势

使用高阶函数对开发人员有一些重要的好处。

首先,高阶函数使代码更加简洁易懂,有助于提高代码的可读性,有助于加快开发过程,使代码更易于使用。

其次,高阶函数有助于将代码组合在一起,使其更易于维护和扩展。

柯理化

卷曲是高阶函数中一种特殊而重要的技术。

利用这一概念,可以从高阶函数中传递不同层次的引号,或为高阶函数添加前缀。

				
					const userLogs = userName => message => console.log(`${userName} -> ${message}`) const log1 = userLogs("Hogan"); log1("Hello World"); log1("Hello") ; const log2 = userLogs("Bobo"); log2("Hello World"); log2("Hello");
				
			

这也是通过一个示例实现的,该示例还为高阶函数提供了两种不同的前缀

结论

本文探讨了函数取向的概念、什么是高阶函数、使用高阶函数的好处、如何在实际应用中使用高阶函数,并对 Curring 的功能做了较小的介绍,还介绍了纯函数。

通过使用高阶函数,开发人员可以编写更智能、模块化的代码,使代码更清晰、更易用。

如果您有任何建议或问题,欢迎留言!

如果您喜欢这一系列文章,请不要犹豫,点击 "喜欢 "并分享,让更多人看到它!

引用

React白話文運動05-高階函式(Higher-order function)

其他文章参考

JavaScript 异步等待 - React 白话运动 03

JavaScript ES6 Object – React 02

JavaScript ES6 - React 01

什么是 Zustand?React 前端状态管理

zh_CN简体中文