JavaScript compose、pipe、柯里化、防抖和节流等大杂烩

2021-07-29

场景分析

需求:正式员工——(日薪*天数) + 绩效工资

我们很快就能写出来了

需求变更:试用期员工——((日薪*天数) + 绩效工资) * 0.8

我们很快又能写出来了

此时我们应该意识到,如果后面还有一大堆“无理取闹”的新需求,而我们以前的函数也无法满足新需求,这个时候不免会出现calculateC、calculateD、、、类似的方法

面向过程

需求变更:对所有员工进行考勤审核

按我们正常逻辑,那肯定是加上万能的if else

compose

那有没有办法不用if else也能实现,终于要引出compose了...

pipe

与compose相对应还有一个pipe

composePromise

如果是执行的方法是Promise,那建议用composePromise

再看一下这两个函数aTest、bTest,都和上面composeTest的执行结果一致,但是composeTest会更好一点(同事说的...)

Promise正常写法

await/async

函数副作用(Side Effects)

函数副作用是指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响.

如下代码,当我们执行foo函数时,函数内部改变了(全局变量)y的值.

副作用可能包含,但不限于以下行为:

  • 更改文件系统
  • 往数据库中插入记录
  • 发送一个 http 请求
  • 改变数据
  • 打印 log
  • 获取用户输入
  • DOM 操作
  • 访问系统状态
  • ...

javaScript内置的一些函数是有副作用的,如pop、push、splice、shift、unshift

我们不能保证禁止函数副作用,而是尽可能避免。

纯函数(Pure Functions)

对于相同的输入,永远得到相同的输出,而且没有任何可观察的副作用。

如上代码,无论我们执行多少次foo1(1),都会得到(1+1=)2,而且并没有影响foo1函数外的任何东西

使用纯函数将会有以下好处:

  • 可缓存性(Cacheable)
  • 可移植性/自文档化(Portable / Self-Documenting)
  • 可测试性(Testable)
  • 合理性(Reasonable)
  • 并行代码(Parallel Code)

常见的函数式编程模型

闭包(Closure)

闭包的用途:可以读取函数内部的变量,而且这些变量的值始终保持在内存中

闭包的弊端:持久化变量不会被正常释放,持续占用内存空间,很容易造成内存浪费,需要手动释放

参考:过程或函数的副作用是一文带你了解 JavaScript 函数式编程weixin_39632057的博客-CSDN博客

什么是闭包?闭包的作用,用法及优缺点 - 为系归舟 - 博客园 (cnblogs.com)

闭包例子:add1(1)(2)(3)(4)()、add2(1,2)(3,4)()求和

参考:一道javascript面试题(闭包与函数柯里化) - Qcer - 博客园 (cnblogs.com)

组合函数

通过多个函数来获取我们期待的值,如上面的compose

如上代码,foo2负责链接字符串,foo3负责转为大写,两个合一起使用即达到期望的效果(例子有点牵强...)

参考资料:JavaScript 函数式编程(一) (juejin.cn)

高阶函数

某个函数(foo3)接收另一个函数(foo2)作为参数,这样的参数称之为高阶函数。

js常见的高阶函数:map,reduce,filter,sort

map
reduce
filter

函数柯里化

柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。 因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。

最简单的例子

以下案例参考:JS实现add(1)(2)(3)(4)_哆姆的博客-CSDN博客

案例1:add(1)(2)(3)(4)、add(1)(2)(3)(4)() 求和

案例2:add(1,2)(3,4)、add(1,2)(3,4,5)() 、add(1)(2)(3)(4)(5)()求和

这两个案例都是利用闭包变量持久化的特点,以及重写函数的toString方法,当我们去调用函数的输出结果时(total+7),或者alert函数的输出结果会调用toString方法。Edge浏览器下console.log(total) 不会输出结果,如下

image-20210729144627551

以 add(1,2)(3,4)为例,执行顺序为 add——>currying——>currying.toStringGIF 2021-7-29 14-42-06

防抖和节流也是柯里化的一个经典应用场景

参考:浅谈 JS 防抖和节流 - SegmentFault 思否

手写js函数节流与抖动 - SegmentFault 思否

防抖

在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时

防抖演示

节流

限制某个事件在指定时间(delay)内仅触发一次,,如果在指定时间内某事件被触发多次,仅一次有效

比如我们限制点击事件间隔1秒执行一次,然后我们一直点击该按钮,可实际生效的事件只有4个而已,如下图

节流演示

防抖节流如何传递事件e和其它参数

以防抖为例

apply、call、bind参考:JS 中 call、apply、bind 那些事 - 掘金 (juejin.cn)