柯里化的应用
- 源码地址:
src\platforms\web**(****weex****)**\runtime\patch.js
- 描述:使用柯里化技巧缓存平台方法
源码
[web 平台]
1 |
|
[weex 平台]
1 |
|
解析
[解决了什么问题]
createPatchFunction
内部定义了一系列的辅助方法,最终返回了一个 patch
方法,这个方法就赋值给了 vm._update
函数里调用的 vm.__patch__
。
由于 patch 是平台相关的,在 Web 和 Weex 环境,它们把虚拟 DOM 映射到 “平台 DOM”的方法是不同的,并且对“DOM”包括的属性和模块创建和更新也不完全相同。因此每个平台都有各自的 nodeOps
和 modules
, 它们的代码需要托管在 src/platforms
这个大目录下。
而不同平台的 patch 的主要逻辑部分是相同的,所以这部分公共代码托管在 core 这个大目录下。差异化部分只需要通过参数来区别,这里就用到了函数柯里化的技巧,通过 createPatchFunction
把差异化参数提前固化,这样不用每次都调用 patch 的时候都传递 nodeOps
和 modules
了。
在这里,nodeOps
表示调 “平台 DOM”的一些操作方法,modules
表示平台的一些模块,他们会在整个 patch 过程的不同阶段执行响应的钩子函数。
柯里化(Currying)
柯里化是一种关于函数的高阶技术,他不仅被用于 Javascript,还被用于其他编程语言。
柯里化一种函数的转换,他是指讲一个函数从可调用的 f(a, b, c)
转换成可调用的 f(a)(b)(c)
。
柯里化不会调用函数,他只是对函数进行转换。
[示例]
柯里化的简单实现
创建一个辅助函数 curry(f)
,该函数将对两个参数的函数 f
执行柯里化。
换句话说,对于两个参数的函数 f(a, b)
执行 curry(f)
会将其转换为 f(a)(b)
形式的函数
1 |
|
如上面代码所示,柯里化的实现非常简单: 只有两个包装器(wrapper)
curry(func) 的结果就是一个包装器 function(a)
当他被向 curriedSum(1) 这样调用时,他的参数会被保存在词法环境中,然后返回一个新的包装器 function(b)。
然后这个包装器被以 2 为参数调用,并且,它将该调用传递给原始的 sum 函数。
柯里化更高级的实现
如 loadsh 库的 _.curry, 会返回一个包装器,改包装器允许函数被正常调用或者以偏函数(partial)的方式调用
1 |
|
[柯里化的目的]
例如,我们有一个用于格式化和输出信息的日志(logging)函数 log(date, importance, message)。
在实际项目中,此类函数具有很多有用的功能, 例如通过网络发送日志(log)。
1 |
|
将 log 方法进行柯里化:
1 |
|
柯里化后,log 仍正常允许:
1 |
|
同时也可以以柯里化的形式运行:
1 |
|
现在可以轻松为当前日志创建便捷函数:
1 |
|
现在,logNow 是具有一个固定第一个参数的 log。
换句话说,就是更简短的 “偏应用函数(partially applied function)”或 “偏函数(partial)”
我们可以更进一步,为当前的调试日志(debug log)提供便捷函数:
1 |
|
所以:
- 柯里化之后,我们没有丢失任何定西:log 依然可以被正常调用
- 我们可以轻松地生成偏函数,例如用于生成今天的日志的偏函数。
[高级柯里化实现]
高级实现
1 |
|
用例
下面是用于多参数函数的“高级”柯里化实现:
1 |
|
解析
curry(func) 调用的结果是如下所示的包装器 curried:
1 |
|
当我们运行它时, 这里有两个 if 执行分支:
如果传入的 args 长度与原始函数所定义的(func.length)相同或者更长,那么只需要使用 func.apply 将调用传递给他即可。
否则,获取一个偏函数:我们目前还没调用 func。取而代之的是返回一个包装器 pass,它将重新应用 curried, 将之传入的参数与新的参数一起传入
最后,我们再次调用它,我们将得到一个新的偏函数(如何没有足够的参数),或者最终的结果
注:
只允许确定参数长度的函数
柯里化要求函数具有固定数量的参数
使用 rest 参数的函数,例如 f(…args), 不能以这种方式进行柯里化。
比柯里化多一点
根据定义,柯里化应该将 sum(a, b, c) 转换为 sum(a)(b)(c)。
但是如前所述,JavaScript 中大多数的柯里化实现都是高级版:他们使得函数可以被多参变体
[总结]
柯里化 是一种转换,将 f(a, b, c) 转换为可以被 f(a)(b)(c) 的形式进行调用。
JavaScript 实现通常都改保持该函数可以被正常调用,并且如果参数不足,则返回偏函数。
柯里化 可以让我们和能够耿荣的获取偏函数。
如日志记录示例:普通函数 log(date, importance, message)在被柯里化之后,当我们调用它的时候传入一个参数(如 log(date))或两个参数(log(date, importance))时,他会返回偏函数。..