不废话,先来看下什么是高阶函数
高阶函数
- 函数可以作为参数传递
- 函数可以作为返回值输出
函数作为参数传递
- 回调函数
- 在ajax异步请求的过程中,回调函数使用的非常频繁
- 在不确定请求返回的时间时,将callback回调函数当成参数传入
- 待请求完成后执行callback函数
下面看个简单的demo:
说实在的本来只是个简单的 ,不过越写越兴奋,就弄成了个小demo了,大家也可以copy下去自己添油加醋一下(写成各种版本),乐呵一下吧,PS:由于代码过多占用文章,将css样式去掉了,样式的实现大家随意发挥就好了
- html结构
<
js部分
// index.js
按照上面的代码啪啪啪,就会得到下面这样的效果,一起来看下吧
不过依然感谢网易云音乐提供的API接口,让我们聆听美妙好音乐
- 好了回归主旋律,前面的戏份有点过了,不知不觉居然写了个小demo,确实有点过分了
- 本来是说一下函数作为参数传递的应用,写的太多了,赶紧调转船头继续讲吧
函数作为返回值输出
亲们,函数作为返回值输出的应用场景那就太多了,这也体现了函数式编程的思想。其实从闭包的例子中我们就已经看到了关于高阶函数的相关内容了,哈哈
还记得在我们去判断数据类型的时候,我们都是通过Object.prototype.toString来计算的。每个数据类型之间只是'[object XXX]'不一样罢了
所以在我们写类型判断的时候,一般都是将参数传入函数中,这里我简单写一下实现,咱们先来看看
function
其实上面实现的isType函数,也属于偏函数的范畴,偏函数实际上是返回了一个包含预处理参数的新函数,以便之后可以调用
另外还有一种叫做预置函数,它的实现原理也很简单,当达到条件时再执行回调函数
function
上面的eat函数只有执行3次的时候才会输出'吃饱了',还是比较形象的。
这种预置函数也是js中巧妙的装饰者模式的实现,装饰者模式在实际开发中也非常有用,再以后的岁月里我也会好好研究之后分享给大家的
好了,不要停,不要停,再来看一个栗子
// 这里我们创建了一个单例模式
这个高阶函数的栗子,可以说一石二鸟啊,既把函数当做参数传递了,又把函数当返回值输出了。
单例模式也是一种非常实用的设计模式,在以后的文章中也会针对这些设计模式去分析的,敬请期待,哈哈,下面再看看高阶函数还有哪些用途
其他应用
函数柯里化
柯里化又称部分求值,柯里化函数会接收一些参数,然后不会立即求值,而是继续返回一个新函数,将传入的参数通过闭包的形式保存,等到被真正求值的时候,再一次性把所有传入的参数进行求值
还能阐述的更简单吗?在一个函数中填充几个参数,然后再返回一个新函数,最后进行求值,没了,是不是说的简单了
说的再简单都不如几行代码演示的清楚明白
// 普通函数
以上代码非常简单,只是起个引导的作用。下面我们来写一个通用的柯里化函数
function
实现了通用的柯里化函数,了不起啊,各位很了不起啊。
不过这还不够,我们还可以利用ES6再来实现一下,请看如下代码:
// ES6版的柯里化函数
两种不同的实现思路相同,之后可以试着分析一下
不过大家有没有发现我们在ES5中使用的bind方法,其实也利用了柯里化的思想,那么再来看一下下
let
为什么这么说?这也看不出什么头绪啊,别捉急,再来看一下bind的实现原理
Function
是不是似曾相识,是不是,是不是,有种师出同门的赶脚了啊
反柯里化
啥?反柯里化,刚刚被柯里化弄的手舞足蹈的,现在又出现了个反柯里化,有木有搞错啊!那么反柯里化是什么呢?简而言之就是函数的借用,天下函数(方法)大家用
比如,一个对象未必只能使用它自身的方法,也可以去借用原本不属于它的方法,要实现这点似乎就很简单了,因为call和apply就可以完成这个任务
(
从以上代码中看出来了,大家都是相亲相爱的一家人。利用call和apply改变了this指向,方法中用到的this再也不局限在原来指定的对象上了,加以泛化后得到更广的适用性
反柯里化的话题是由我们亲爱的js之父发表的,我们来从实际例子中去看一下它的作用
let slice = Array.prototype.slice.uncurrying();
(function() {
let result = slice(arguments);
// 这里只需要调用slice函数即可
console.log(result); // [1, 2, 3]
})(1,2,3);
以上代码通过反柯里化的方式,把Array.prototype.slice变成了一个通用的slice函数,这样就不会局限于仅对数组进行操作了,也从而将函数调用显得更为简洁清晰了
最后再来看一下它的实现方式吧,看代码,更逼真
Function
其实实现反柯里化的方式不只一种,下面再给大家分享一种,直接看代码
Function
实现方式大致相同,大家也可以写一下试试,动动手,活动一下筋骨
函数节流
下面再说一下函数节流,我们都知道在onresize、onscroll和mousemove,上传文件这样的场景下,函数会被频繁的触发,这样很消耗性能,浏览器也会吃不消的
于是大家开始研究一种高级的方法,那就是控制函数被触发的频率,也就是函数节流了。简单说一下原理,利用setTimeout在一定的时间内,函数只触发一次,这样大大降低了频率问题
函数节流的实现也多种多样,这里我们实现大家常用的吧
function
给页面上body设置一个高度出现滚动条后试试看,比每滚动一下就触发来说,大大降低了性能的损耗,这就是函数节流的作用,起到了事半功倍的效果,开发中也比较常用的
分时函数
我们知道有一个典故叫做:罗马不是一天建成的;更为通俗的来说,胖纸也不是一天吃成的
体现在程序里也是一样,我们如果一次获得了很多数据(比如有10W数据),然后在前端渲染的时候会卡到爆,浏览器那么温柔的物种都会起来骂娘了
所以在处理这么多数据的时候,我们可以选择分批进行,不用一次塞辣么多,嘴就辣么大
下面来看一下简单的实现
function
惰性加载
兼容现代浏览器以及IE浏览器的事件添加方法就是一个很好的栗子
// 常规的是这样写的
这样实现有一个缺点,就是在调用addEvent的时候都会执行分支条件里,其实只需要判断一次就行了,非要每次执行都来一波
下面我们再来优化一下addEvent,以规避上面的缺点,就是我们要实现的惰性加载函数了
let
上面的addEvent函数还是个普通函数,还是有分支判断。不过当第一次进入分支条件后,在内部就会重写了addEvent函数
下次再进入addEvent函数的时候,函数里就不存在条件判断了
终点
节目不早,时间刚好,又到了该要说再见的时候了,来一个结束语吧
高阶函数
- 可以把函数当做参数传递和返回值输出
- 函数柯里化
- 接收参数,返回新函数,把参数传给新函数,最后求值
- 定义
- 作用
- 参数复用 (add函数栗子)
- 提前返回 (惰性加载)
- 延迟计算 (bind)
- 反柯里化
- 统一方法,让天下没有不能用的方法
- 函数节流
- 将频繁调用的函数设定在一个时间内执行,防止多次触发
- 分时函数
- 一次性加载太多太多数据,吃不消,可以像node中流一样,慢慢来,别急
- 惰性加载
- 函数执行的分支仅会发生一次