柯里化(Curring, 以逻辑学家Haskell Curry命名)
写在开头
柯里化理解的基础来源于我们前几篇文章构建的知识,如果还未能掌握闭包,建议回阅前文。
代码例子会用到 apply/call
,一般用来实现对象冒充,例如字符串冒充数组对象,让字符串拥有数组的方法。待对象讲解篇会细分解析。在此先了解,两者功能相同,区别在于参数传递方式的不同, apply
参数以数组方式传递,call
多个参数则是逗号隔开。
|
|
代码例子中使用到了ES6语法,对ES6还不熟悉的话,可学习社区这篇文章:《30分钟掌握ES6/ES2015核心内容(上)》
函数柯里化
函数柯里化在JavaScript中其实是高阶函数的一种应用,上篇文章我们简略介绍了高阶函数(可以作为参数传递,或作为返回值)。
理论知识太枯燥,来个生活小例子,”存款买房”(富二代绕道)。假设买房是我们存钱的终极目标。那么在买房前,存在卡里的钱(老婆本)就不能动。等到够钱买房了,钱从银行卡取出来,开始买买买。。。
函数柯里化就像我们往卡里存钱,存够了,才能执行买房操作,存不够,接着存。
函数柯里化公式
先上几个公式(左边是普通函数,右边就是转化后柯里化函数支持的调用方式):
|
|
两种公式类型的区别 —— 函数触发执行的机制不同:
- 公式一当传入参数等于函数参数数量时开始执行
- 公式二当没有参数传入时(且参数数量满足)开始执行
通过公式,我们先来理解这行代码 fn(a)(b)(c)(d)
, 执行 fn(a)
时返回的是一个函数,并且支持传参。何时返回的是值而不是函数的触发机制控制权在我们手里,我们可以为函数制定不同的触发机制。
普通的函数调用,一次性传入参数就执行。而通过柯里化,它可以帮我们实现函数部分参数传入执行(并未立即执行原始函数,钱没存够接着存),这就是函数柯里化的特点:“延迟执行和部分求值”
“函数柯里化:指封装一个函数,接收原始函数作为参数传入,并返回一个能够接收并处理剩余参数的函数”
函数柯里化的例子
|
|
|
|
分析代码知识点:
- 函数作为返回值返回,闭包形成,外部环境可访问函数内部作用域
- 子函数可访问父函数的作用域,作用域由内而外的作用域链查找规则,作用域嵌套形成
- 在函数参数数量不满足时,返回一个函数(该函数可接收并处理剩余参数)
- 当函数数量满足我们的触发机制(可自由制定),触发原始函数执行
前几篇文章的知识点此时刚好。可见基础知识的重要性,高阶的东西始终要靠小砖头堆砌出来。
弄清原理后,接下来就是将代码写得更通用些(高大上些)。
|
|
|
|
为实现公式中不同的两种调用公式,两个createCurry方法制定了两种不同的触发机制。记住一个点,函数触发机制可根据需求自行制定。
偏函数与柯里化的区别
先上个公式看对比:
|
|
“函数柯里化中,当你传入部分参数时,返回的并不是原始函数的执行结果,而是一个可以继续支持后续参数的函数。而偏函数的调用方式更像是普通函数的调用方式,只调用一次,它通过原始函数内部来实现不定参数的支持。”
如果已经看懂上述柯里化的代码例子,那么改写支持偏函数的代码,并不难。
|
|
使用ES6函数参数默认值,为没有传入参数,指定默认值为0,支持无参数或不定参数传入。
柯里化的特点:
- 参数复用(固定易变因素)
- 延迟执行
- 提前返回
柯里化的缺点
柯里化是牺牲了部分性能来实现的,可能带来的性能损耗:
- 存取
arguments
对象要比存取命名参数要慢一些 - 老版本浏览器在
arguments.lengths
的实现相当慢(新版本浏览器忽略) fn.apply()
和fn.call()
要比直接调用fn()
慢- 大量嵌套的作用域和闭包会带来开销,影响内存占用和作用域链查找速度
柯里化的应用
- 利用柯里化制定约束条件,管控触发机制
- 处理浏览器兼容(参数复用实现一次性判断)
- 函数节流防抖(延迟执行)
- ES5前bind方法的实现
一个应用例子:浏览器事件绑定的兼容处理
|
|
秒懂反柯里化
先上公式,从来没有这么喜欢写公式,简明易懂。
看完公式,是不是似曾相识,这不就是我们日常敲码的普通函数么?没错的,函数柯里化就是把普通函数变成成一个复杂的函数,而反柯里化其就是柯里化的逆反,把复杂变得简单。
函数柯里化是把支持多个参数的函数变成接收单一参数的函数,并返回一个函数能接收处理剩余参数:fn(a,b,c,d) => fn(a)(b)(c)(d)
,而反柯里化就是把参数全部释放出来:fn(a)(b)(c)(d) => fn(a,b,c,d)
。
|
|
反思:为何要使用柯里化
函数柯里化是函数编程中的一个重要的基础,它为我们提供了一种编程的思维方式。显然,它让我们的函数处理变得复杂,代码调用方式并不直观,还加入了闭包,多层作用域嵌套,会有一些性能上的影响。
但在一些复杂的业务逻辑封装中,函数柯里化能够为我们提供更好的应对方案,让我们的函数更具自由度和灵活性。
实际开发中,如果你的逻辑处理相对复杂,不妨换个思维,用函数柯里化来实现,技能包不嫌多。
说到底,程序员就是解决问题的那群人。
写在结尾
本篇函数柯里化知识点的理解确实存在难度,暂时跳过这章也无妨,可以先了解再深入。耐得主寂寞的小伙伴回头多啃几遍,没准春季面试就遇到了。
参考文档:
本文首发Github,期待Star!
https://github.com/ZengLingYong/blog
作者:以乐之名
本文原创,有不当的地方欢迎指出。转载请指明出处。