Javascript中的高阶函数
高阶函数是指以函数作为参数的函数,并且可以将函数作为结果返回的函数。
1. 高阶函数
- 接受一个或多个函数作为输入
- 输出一个函数
至少满足以上一个条件的函数
在js的内置对象中同样存在着一些高阶函数,像数组的map
,filter
,reduce
方法等,它们接受一个函数作为参数,并应用这个函数到列表的每一个元素
1.1 map
map
方法接收一个函数作为参数 ,遍历数组,并且返回一个新的数组,新的数组里的每个元素都执行map
传入的函数。
|
|
返回的是一个新数组arr1
,==不改变原数组==
注意
:如果传入的参数没有返回值,则数组的每一项都会是undefind
经典题目
|
|
来看看上面这个代码输出什么
答案:[1, NaN, NaN]
解析
parseInt()
函数可解析一个字符串,并返回一个整数。
基数radix可选2到36之间的整数,当参数 radix 的值为 0,或没有设置该参数时,parseInt()
会根据该字符串来判断数字的基数。
当忽略参数 radix , 默认的基数如下:
- 如果 字符串 以 “0x” 开头,
parseInt()
会把 其余部分解析为十六进制的整数。 - 如果字符串以 0 开头,把其余部分解析为八进制或十六进制的数字。
- 如果字符串以 1 ~ 9 的数字开头,
parseInt()
将把它解析为十进制的整数
如果基数小于2或大于36,或者第一个非空白字符不能转换为数字,则返回NaN。
注意
:基数可不是默认十进制
当我们把数组传入parseInt
时,由于接收2个参数,会将数组的索引作为基数传给parseInt
,所以实质上进行的是以下几步
|
|
第一个parseInt,字符串为1,基数为0 被默认为十进制,所以返回1
第二个和第三个,因为基数不是0,且小于2,所以parseInt()
会返回 NaN
。
1.2 filter
用于筛选数组
filter
方法接收一个函数作为参数,通过这个函数来指定筛选数组的规则,最后返回满足规则的新数组
在传入的函数中有3个参数可选
参数 | 描述 |
---|---|
currentValue | 必须。当前元素的值 |
index | 可选。当前元素的索引值 |
arr | 可选。当前元素属于的数组对象 |
注意
:
- 不会检测空数组
- 不会改变原始数组
|
|
1.3 reduce
reduce
能做的事情很多,但是我们平时都使用for循环之类的方法代替了,但是reduce
真的高逼格
|
|
以上是在w3school
中给出的reduce语法,这里我们常用的只有前面两个
参数 | 描述 |
---|---|
total | 必需。初始值, 或者计算结束后的返回值。 |
currentValue | 必需。当前元素 |
|
|
从第四行的调试中可以看出reduce
函数的执行过程,在没有初始值的情况下,将数组第一个值作为value
第二个值作为item
再依次往下遍历整个数组,将返回值作为value
,数组的下一位作为item
,直至遍历完成。
利用ruduce
实现数组去重
|
|
-
通过将空数组作为
prev
初始值 -
再通过
indexOf
判断数组中是否包含item
,如果该元素不存在就返回-1, -
&&运算符第一条语句为true,则执行第二条,否则不执行
-
prev.push(item) 将
item
加入prev数组,最终返回数组prev
注意:
indexOf()方法返回在数组中可以找到给定元素的第一个索引,如果该元素不存在,则返回-1。
关于
&&
运算符,第一条语句为true则执行第二条,否则不执行
ruduce
的用法远不止这些,有兴趣的可以再了解以下~
还有很多内置对象都是高阶函数,这里就不一一说明了,从上面的三个方法中,已经能很直观的感受到了函数接收函数作为参数,再返回值的过程,逼格很高也很好用
2. AOP面向切面编程
当我们需要使用一个公共函数,并且需要在这个函数执行前后添加自己的逻辑,通常我们的做法不能是直接修改这个函数,因为它是==公共函数==,这时候我们可以通过AOP的方法利用高阶函数和原型链的特点进行处理
把一些与业务无关的功能抽离出来,通过"动态植入"的方法,掺入到业务逻辑模块中。这样做的好处是保证业务逻辑模块的纯净和高内聚,其次可以方便的复用功能模块
需求:实现在函数==执行前==输出提示信息
这是一个函数
|
|
实现在函数==执行前==输出提示信息
|
|
如果需要实现==后置通知==,只需要将callback()和this(…args)调换一下就可以了
|
|
实现的原理
在调用公共函数时,传入我们需要执行提前执行的函数,在内部函数执行前先调用该函数
3.偏函数
当一个函数有很多参数时,调用该函数就需要提供多个参数,如果可以减少参数的个数,就能简化该函数的调用,降低调用该函数的难度。
- 实现3个数求和
|
|
在调用时我们需要传入3个参数,好像有些许麻烦,下面我们用偏函数的做法
创建一个新的partial
函数,这个新函数可以固定住原函数的部分参数,从而减少调用时的输入的参数,让我们的调用更加简单
|
|
高阶函数除了可以接收函数作为参数外,还可以将函数作为结果返回,偏函数就是固定了函数的一个或多个参数,返回一个新的函数接收剩下的参数,以此来简化函数的调用。
Function.prototype.bind
函数就是一个偏函数的典型代表,它接受的第二个参数开始,为预先添加到绑定函数的参数列表中的参数
4. 函数柯里化
与偏函数不同,柯里化是把接收多个参数的函数转换成多个只接收一个参数的函数。
我们从一个简单的例子来认识函数柯里化
|
|
接下来我们来看看利用柯里化来实现
|
|
4.1 函数柯里化的作用
要真正理解柯里化还是得看示例
4.1.1 参数复用
我们先看一段短短的代码,这段代码中,实现了输入输出个人信息的功能,通过myInfo
函数将参数拼接返回,这实际上很简单,但是当有很多很多的用户信息时,需要一直传递着个人信息
这个参数,这样显然是不合理的
|
|
下面我们通过柯里化技术来解决
|
|
这个就是柯里化技术的作用之一了,参数复用,个人感觉还是很好用的
在上面代码的基础上,我们可以继续扩展我们的信息,就像这样,利用一个函数就可以实现多个功能
|
|
4.1.2 提前返回
这个特性是用来对浏览器的监听事件兼容性做一些判断并初始化,解决有些浏览器对addEventListener
存在的兼容性问题,所以在使用之前做一次判断,之后就可以省略了
|
|
由于使用了立即执行函数,即使触发多次事件依旧只会触发一次if条件判断
4.1.3 延迟执行
下面我们通过一道例题来了解
编写一个add
函数实现下面的功能
add(1)(2)(3) = 6
add(1, 2, 3)(4) = 10
add(1)(2)(3)(4)(5) = 15
|
|
这段代码中涵盖的知识面很多,核心的部分在于inner.toString
这里,利用了当返回一个函数时返回的是它的字符串形式,所以我们可以利用这个特性来自定义我们的返回值