JS函数柯里化
柯里化是什么
维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
这个解释有些抽象 我们先来看一个简单的实现
// 柯里化之前
function add(x, y) {
return x + y;
}
// 柯里化之后
function currying(x) {
return function(y) {
return x + y;
}
}
add(1, 2)
currying(1)(2)
优点
- 参数复用
// 柯里化前
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test')
// 柯里化后
function curryingCheck(reg) {
return function(txt) {
reg.test(txt)
}
}
const hasNumber = curryingCheck(/\d+/g)
hasNumber('123')
- 提前确认
// dom操作封装
var on = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false)
}
}
} else {
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler)
}
}
}
})()
- 延迟执行
Function.prototye.bind = function(context) {
const _this = this;
const args = Array.prototype.slice.call(arguments, 1)
return function() {
return _this.apply(context, args)
}
}
柯里化的初步封装
const currying = function(fn) {
const args = Array.prototype.slice.call(arguments, 1)
return function() {
const newArgs = args.concat(...arguemnts)
}
return fn.apply(this, newArgs)
}
上面这样封装之后还是存在缺陷,最多只能拓展一个参数,像currying(a)(b)(c)这样的就不支持了。
另外一种对原函数进行柯里化处理,支持多参数调用
function processCurrying(fn, args) {
const len = fn.length;
const args = args || []
const _this = this
return function() {
let _args = Array.prototype.slice.call(arguments, 1)
Array.prototype.push.apply(args, _args)
if (args.length < len) {
return processCurrying.call(_this, fn, _args)
}
return fn.apply(this, _args)
}
}
性能问题
- 存取arguments对象通常要比存取命名参数要慢一点
- 一些老版本的浏览器在arguments.length的实现上是相当慢的
- 使用fn.apply()和fn.call()通常来说比直接调用fn()稍微慢点
- 创建大量嵌套作用域和闭包函数会带来开销,无论是在内存上还是速度上
大部分应用中主要瓶颈还是在操作DOM节点上,所以js的性能损耗基本可以忽略不计,柯里化可以放心使用。
经典面试题
// 实现一个add方法,使计算结果能够满足如下预期
add(1)(2)(3) == 6
add(1,2,3)(4) == 10
add(1)(2)(3)(4)(5) == 15
// 实现如下
function add() {
const args = Array.prototype.slice.call(arguments)
function _adder() {
args.push(...arguments)
return _adder
}
// 当执行 add(1)(2)(3) == 6 时会进行隐式类型转换
_adder.toString = function() {
return args.reduce((pre, cur) => {
return pre + cur
})
}
return _adder
}