JS中的高阶函数

高阶函数是指以函数作为参数的函数,并且可以将函数作为结果返回的函数。

1. 高阶函数

  • 接受一个或多个函数作为输入
  • 输出一个函数

至少满足以上一个条件的函数

在js的内置对象中同样存在着一些高阶函数,像数组的mapfilterreduce方法等,它们接受一个函数作为参数,并应用这个函数到列表的每一个元素

1.1 map

map方法接收一个函数作为参数 ,遍历数组,并且返回一个新的数组,新的数组里的每个元素都执行map传入的函数。

let arr = [1, 2, 3, 4];
let arr1 = arr.map(item => item * 2)
console.log(arr1);// [2, 4, 6, 8]

返回的是一个新数组arr1,不改变原数组

注意:如果传入的参数没有返回值,则数组的每一项都会是undefind

经典题目

console.log(['1','2','3'].map(parseInt));

来看看上面这个代码输出什么

答案[1, NaN, NaN]

解析

parseInt() 函数可解析一个字符串,并返回一个整数。

当参数 radix 的值为 0,或没有设置该参数时,parseInt()会根据该字符串来判断数字的基数。

当忽略参数 radix , 默认的基数如下:

  • 如果 字符串 以 “0x” 开头,parseInt() 会把 其余部分解析为十六进制的整数。
  • 如果字符串以 0 开头,把其余部分解析为八进制或十六进制的数字。
  • 如果字符串以 1 ~ 9 的数字开头,parseInt()将把它解析为十进制的整数

注意:基数可不是默认十进制噢!

当我们把数组传入parseInt时,由于接收2个参数,会将数组的索引作为基数传个parseInt,所以实质上进行的是以下几步

parseInt('1', 0)
parseInt('2', 1)
parseInt('3', 2)

注意:如果字符串的第一个字符不能被转换为数字,那么parseInt()会返回 NaN。

小tips

parseInt()还有很多值得注意的问题,可以使用搜索引擎再了解以下

1.2 filter

用于筛选数组

filter方法接收一个函数作为参数,通过这个函数来指定筛选数组的规则,最后返回满足规则的新数组

在传入的函数中有3个参数可选

参数 描述
currentValue 必须。当前元素的值
index 可选。当前元素的索引值
arr 可选。当前元素属于的数组对象

注意

  • 不会检测空数组
  • 不会改变原始数组
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let arr1 = arr.filter(num => {
return num > 5
})
console.log(arr1);// [6, 7, 8, 9]

1.3 reduce

reduce能做的事情很多,但是我们平时都使用for循环之类的方法代替了,但是reduce真的高逼格

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

w3school中给出的reduce语法,这里我们常用的只有前面两个

参数 描述
total 必需。初始值, 或者计算结束后的返回值。
currentValue 必需。当前元素
let arr = [1, 2, 3, 4]
let sum = arr.reduce((value, item) => {
console.log(value, item);
// 1 2 3 3 6 4
return value + item
})
console.log(sum);// 10

从第四行的调试中可以看出reduce函数的执行过程,在没有初始值的情况下,将数组第一个值作为value第二个值作为item再依次往下遍历整个数组,将返回值作为value,数组的下一位作为item,直至遍历完成。

利用ruduce实现数组去重

let arr = [1,1,2,3,4,2,5,4];
let unique = arr.reduce(function (prev, item) {
prev.indexOf(item) === -1 && prev.push(item);
return prev;
}, []);
console.log(unique); // [1, 2, 3, 4, 5]

通过将空数组作为prev初始值,再通过indexOf判断数组中是否包含item,如果没有就将item加入数组,最终返回数组

关于&&运算符,第一条语句为true则执行第二条,否则不执行

ruduce的用法远不止这些,有兴趣的可以再了解以下~


还有很多内置对象都是高阶函数,这里就不一一说明了,从上面的三个方法中,已经能很直观的感受到了函数接收函数作为参数,再返回值的过程,逼格很高也很好用

2. AOP 面向切面编程

当我们需要使用一个公共函数,并且需要在这个函数执行前后添加自己的逻辑,通常我们的做法不能是直接修改这个函数,因为它是公共函数,这时候我们可以通过AOP的方法利用高阶函数和原型链的特点进行处理

把一些与业务无关的功能抽离出来,通过"动态植入"的方法,掺入到业务逻辑模块中。这样做的好处是保证业务逻辑模块的纯净和高内聚,其次可以方便的复用功能模块

  • 下面我们要实现在函数执行前输出提示信息
function say(who) {
console.log(who + ':函数执行了');
}
Function.prototype.before = function(callback) {
return (...args) => {
callback()
this(...args)
}
}
let whoSay = say.before(function() {
console.log('你要被调用了');
})
whoSay('ljc')
// 你要被调用了
// ljc:函数执行了

如果需要实现后置通知,只需要将6,7行换以下就可以了

实现的原理

在调用公共函数时,传入我们需要执行提前执行的函数,在内部函数执行前先调用该函数

3. 偏函数

当一个函数有很多参数时,调用该函数就需要提供多个参数,如果可以减少参数的个数,就能简化该函数的调用,降低调用该函数的难度。

  • 实现3个数求和
function sum(a, b, c){
return a + b + c;
}
sum(1, 2, 3) // 6

在调用时我们需要传入3个参数,好像有些许麻烦,下面我们用偏函数的做法

创建一个新的partial函数,这个新函数可以固定住原函数的部分参数,从而减少调用时的输入的参数,让我们的调用更加简单

function sum(a, b, c) {
return a + b + c
} function partial(sum, c) {
return function (a, b) {
return sum(a, b, c)
}
}
let partialSum = partial(sum, 3)// -> 6

高阶函数除了可以接收函数作为参数外,还可以将函数作为结果返回,偏函数就是固定了函数的一个或多个参数,返回一个新的函数接收剩下的参数,以此来简化函数的调用。

Function.prototype.bind 函数就是一个偏函数的典型代表,它接受的第二个参数开始,为预先添加到绑定函数的参数列表中的参数

4. 函数柯里化

与偏函数不同,柯里化是把接收多个参数的函数转换成多个只接收一个参数的函数。

我们从一个简单的例子来认识函数柯里化

function add(a, b) {
return a + b;
}
add(1, 2) // 3 普通做法 一次传入两个参数 // 假设有一个 curring 函数可以做到柯里化
function curring(){}
curring(1)(2) // 我们通过这样的方式来接受参数,这样就实现了柯里化

接下来我们来看看利用柯里化来实现

function curring(x) {
return return y => x + y
}
curring(1)(2) // => 3

4.1 函数柯里化的作用

要真正理解柯里化还是得看示例

4.1.1 参数复用

我们先看一段短短的代码,这段代码中,实现了输入输出个人信息的功能,通过myInfo函数将参数拼接返回,这实际上很简单,但是当用很多很多的用户信息时,需要一直传递着个人信息这个参数,这样显然是不合理的

function myInfo(inf, name, age) {
return `${inf}:${name}${age}`
}
const myInfo1 = myInfo('个人信息', 'ljc', '19')
console.log(myInfo1); // 个人信息:ljc19

下面我们通过柯里化技术来解决

function myInfoCurry(inf) {
return (name, age) => {
return `${inf}:${name}${age}`
}
}
let myInfoName = myInfoCurry('个人信息')
const myInfo1 = myInfoName('ljc', '19')
const myInfo2 = myInfoName('ljcc','19')
console.log(myInfo2); // 个人信息:ljcc119
console.log(myInfo1); // 个人信息:ljc19

这个就是柯里化技术的作用之一了,参数复用,个人感觉还是很好用的

在上面代码的基础上,我们可以继续扩展我们的信息,就像这样,利用一个函数就可以实现多个功能

let myInfoSex = myInfoCurry('爱好')
const myInfo3 = myInfoSex('看球赛','听歌')
console.log(myInfo3); // 爱好:看球赛听歌

4.1.2 提前返回

这个特性是用来对浏览器的监听事件兼容性做一些判断并初始化,解决有些浏览器对addEventListener存在的兼容性问题,所以在使用之前做一次判断,之后就可以省略了

const whichEvent = (function () {
if (window.addEventListener) {
return function (ele, type, listener, useCapture) {
ele.addEventListener(type, function (e) {
listener.call(ele, e)
}, useCapture)
}
} else if (window.attachEvent) {
return function (ele, type, handler) {
ele.attachEvent('on' + type, function (e) {
handler.call(ele, e)
})
}
}
})()

由于使用了立即执行函数,即使触发多次事件依旧只会触发一次if条件判断

4.1.3 延迟执行

下面我们通过一道例题来了解

编写一个add函数实现下面的功能

add(1)(2)(3) = 6

add(1, 2, 3)(4) = 10

add(1)(2)(3)(4)(5) = 15

function add(...args) {
let inner = function () {
args.push(...arguments);
inner.toString = function () {
return args.reduce((prev, cur) => {
return prev + cur
})
}
return inner
}
return inner
}
console.log(add(1)(2)(3)); // f 6

这段代码中涵盖的知识面很多,核心的部分在于inner.toString这里,利用了当返回一个函数时返回的是它的字符串形式,所以我们可以利用这个特性来自定义我们的返回值


以上就是关于高阶函数的全部内容了,这部分的知识有点难,可能理解的不够深入,如果有什么好的方法,可以留言讨论一下

参考文献:JavaScript Web前端开发指南

部分代码实现学习于b站,以及GitHub等平台

JS中的高阶函数的更多相关文章

  1. Python 函数式编程 & Python中的高阶函数map reduce filter 和sorted

    1. 函数式编程 1)概念 函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念.wiki 我们知道,对象是面向对象的第一型,那么函数式编程也是一样,函数是函数 ...

  2. Python中的高阶函数与匿名函数

    Python中的高阶函数与匿名函数 高阶函数 高阶函数就是把函数当做参数传递的一种函数.其与C#中的委托有点相似,个人认为. def add(x,y,f): return f( x)+ f( y) p ...

  3. Java中的函数式编程(五)Java集合框架中的高阶函数

    写在前面 随着Java 8引入了函数式接口和lambda表达式,Java 8中的集合框架(Java Collections Framework, JCF)也增加相应的接口以适应函数式编程.   本文的 ...

  4. [Node.js] 闭包和高阶函数

    原文地址:http://www.moye.me/2014/12/29/closure_higher-order-function/ 引子 最近发现一个问题:一部分写JS的人,其实对于函数式编程的概念并 ...

  5. javascript中的高阶函数, 和 类定义Function, 和apply的使用

    参考: http://www.cnblogs.com/delin/archive/2010/06/17/1759695.html js中的类, 也是用function关键字来定义的: function ...

  6. 如何在JavaScript中使用高阶函数

    将另一个函数作为参数的函数,或者定义一个函数作为返回值的函数,被称为高阶函数. JavaScript可以接受高阶函数.这种处理高阶函数的能力以及其他特点,使JavaScript成为非常适合函数式编程的 ...

  7. ES6中的高阶函数:如同 a => b => c 一样简单

    作者:Sequoia McDowell 2016年01月16日 ES6来啦!随着越来越多的代码库和思潮引领者开始在他们的代码中使用ES6,以往被认为是"仅需了解"的ES6特性变成了 ...

  8. JavaScript中的高阶函数

    之前写的<JavaScript学习手册>,客户跟我说有些内容不适合初学者,让我删了,感觉挺可惜的,拿到这里和大家分享. JavaScript中的一切都是对象,这句话同样适用于函数.函数对象 ...

  9. python中的高阶函数

    高阶函数英文叫Higher-order function.什么是高阶函数?我们以实际代码为例子,一步一步深入概念. 变量可以指向函数 以Python内置的求绝对值的函数abs()为例,调用该函数用以下 ...

随机推荐

  1. Go-26-Json

    JSON(JavaScript Object Notation,JavaScript对象表示法)是一种轻量级的数据交换格式,因简单.可读性强被广泛使用. Go的标准包encoding/json对JSO ...

  2. surging 如何使用流媒体服务

    前言 随着直播行业大火,各种直播类产品和产品层出不穷,能够满足各方人员的需求和互动,也使得斗鱼.虎牙.抖音都随着直播业的大火而欣欣向荣, 大家也对直播平台了解不少,也参与使用,但是怎么样才能研发出视频 ...

  3. JDK8新特性(二) 流式编程Stream

    流式编程是1.8中的新特性,基于常用的四种函数式接口以及Lambda表达式对集合类数据进行类似流水线一般的操作 流式编程分为大概三个步骤:获取流 → 操作流 → 返回操作结果 流的获取方式 这里先了解 ...

  4. 1035 Password

    To prepare for PAT, the judge sometimes has to generate random passwords for the users. The problem ...

  5. Python中Selenium模块的使用

    目录 Selenium的介绍.配置和调用 Selenium的配置 Selenium的调用 Selenium的使用 定位 定位元素的使用 定位下拉标签元素 在iframe框架之间切换 上传文件 Webd ...

  6. MySQL UDF提权执行系统命令

    目录 UDF UDF提权步骤 UDF提权复现(php环境) UDF UDF (user defined function),即用户自定义函数.是通过添加新函数,对MySQL的功能进行扩充,其实就像使用 ...

  7. 在进程空间使用虚拟内存(Windows 核心编程)

    虚拟内存空间 如今的 Windows 操作系统不仅可以运行多个应用程序,还可以让每一个应用程序享受到约 4 GB 的虚拟内存空间(包括系统占用),假如内存为 4 GB 的话.那为什么 Window 可 ...

  8. Run-Time Check Failure #0,The value of ESP was not properly saved 错误解决

    调用DLL函数,出现错误 Run-Time Check Failure #0 - The value of ESP was not properly saved across a function c ...

  9. 【python】Leetcode每日一题-颠倒二进制位

    [python]Leetcode每日一题-颠倒二进制位 [题目描述] 颠倒给定的 32 位无符号整数的二进制位. 示例1: 输入: 00000010100101000001111010011100 输 ...

  10. 第三部分 IDEA创建并运行项目

    可以创建一个maven,几行代码就解决了导入依赖,但是我的电脑不知道哪里出现了问题,IDEA重装,jdk重装,maven重装,都无法解决问题,找了3天,还是没有解决问题.最后只能采用手动导入包方法.看 ...