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. try - with - resource

    本文详细介绍了自 JDK 7 引入的 try-with-resources 语句的原理和用法,以及介绍了 JDK 9 对 try-with-resources 的改进,使得用户可以更加方便.简洁的使用 ...

  2. Digit Generator UVA - 1583

    ​ For a positive integer N, the digit-sum of N is defined as the sum of N itself and its digits. Whe ...

  3. JavaScript实现斐波那契数列

    什么是斐波那契数列 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为&qu ...

  4. IdentityServer4+OAuth2.0+OpenId Connect 详解

    一  Oauth 2.0 1 定义 OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. ...

  5. 【手打】coredns单台使用

    目录: coredns介绍 coredns安装 corendns配置 coredns介绍 CoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,所以很多开源项目以及工程师 ...

  6. <input type="file" id="fileID">文本框里的值清空方法

    一般情况下,不允许通过脚本来对文件上传框赋值. 下面是一个变通的方法.就是创建一个新的input type="file" 把原来的替换掉. <!DOCTYPE html PU ...

  7. Hack The Box - Archetype

    攻略的话在靶场内都有,也有官方的攻略,我作为一个技术小白,只是想把自己的通关过程记录下来,没有网站内大佬们写得好 我们获得了一个IP: 尝试访问了一下,应该不存在web页面: 对常规端口进行一个扫描: ...

  8. [LeetCode每日一题]781. 森林中的兔子

    [LeetCode每日一题]781. 森林中的兔子 问题 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers 数组里. ...

  9. 使用jenkins一键打包发布vue项目

    jenkins的安装 Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建.测试和部署软件. Jenkins 支持各种运行方式,可通过系统包.Docker 或者通过一个独立 ...

  10. 【Web前端HTML5&CSS3】03-字符实体与语义标签

    笔记来源:尚硅谷Web前端HTML5&CSS3初学者零基础入门全套完整版 目录 字符实体与语义标签 1. 字符实体 2. meta标签 3. 语义标签 4. 块元素与行内元素 块元素(bloc ...