JS编码技巧--函数柯里化

169 阅读1分钟

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)
  }
}

性能问题

  1. 存取arguments对象通常要比存取命名参数要慢一点
  2. 一些老版本的浏览器在arguments.length的实现上是相当慢的
  3. 使用fn.apply()和fn.call()通常来说比直接调用fn()稍微慢点
  4. 创建大量嵌套作用域和闭包函数会带来开销,无论是在内存上还是速度上

大部分应用中主要瓶颈还是在操作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
}