js函数柯里化的理解

函数柯里化理解示例

// 求和
let add = function (a, b, c) {
  return a + b + c
}

{
  /* 简单版curry函数示例 */

  // 对求和函数做curry化
  let f1 = curry(add, 1, 2, 3)
  console.log('简单版', f1())// 6

  // 对求和函数做curry化
  let f2 = curry(add, 1, 2)
  console.log('简单版', f2(3))// 6

  // 对求和函数做curry化
  let f3 = curry(add)
  console.log('简单版', f3(1, 2, 3))// 6

  // 但是简单版curry函数多次调用会报错,如下:
  // console.log('简单版',f3(1)(2)(3))// Uncaught TypeError: f3(...) is not a function

  // 简单版(只能调用1次)
  function curry(fn) {
    // 缓存除函数fn之外的所有参数
    let args = Array.prototype.slice.call(arguments, 1)
    // 闭包
    return function () {
      // 连接已缓存的老的参数和新传入的参数(即把每次传入的参数全部先保存下来,但是并不执行)
      let newArgs = args.concat(Array.from(arguments))
      // 调用,这里的apply只是为了方便传参数,可以不用
      return fn.apply(null, newArgs)
    }
  }
}

{
  /* 复杂版curry函数示例 */

  // 对求和函数做curry化
  let f1 = curry(add, 1, 2, 3)
  console.log('复杂版', f1())// 6

  // 对求和函数做curry化
  let f2 = curry(add, 1, 2)
  console.log('复杂版', f2(3))// 6

  // 对求和函数做curry化
  let f3 = curry(add)
  console.log('复杂版', f3(1, 2, 3))// 6

  // 复杂版curry函数可以多次调用,如下:
  console.log('复杂版', f3(1)(2)(3))// 6
  console.log('复杂版', f3(1, 2)(3))// 6
  console.log('复杂版', f3(1)(2, 3))// 6

  // 复杂版(每次可传入不定数量的参数,当所传参数总数不少于函数的形参总数时,才会执行)
  function curry(fn) {
    // 闭包
    // 缓存除函数fn之外的所有参数
    let args = Array.prototype.slice.call(arguments, 1)
    return function () {
      // 连接已缓存的老的参数和新传入的参数(即把每次传入的参数全部先保存下来,但是并不执行)
      let newArgs = args.concat(Array.from(arguments))
      if (newArgs.length < fn.length) {// 累积的参数总数少于fn形参总数
        // 递归传入fn和已累积的参数,这里的call只是为了方便传参数,可以不用
        return curry.call(null, fn, ...newArgs)
      } else {
        // 调用,这里的apply只是为了方便传参数,可以不用
        return fn.apply(null, newArgs)
      }
    }
  }
}

参数解构版本:


function curry(fn, ...args) {
  return function(...newArgs) {
    let allArgs = args.concat(newArgs)

    if(allArgs.length

前端开发者进阶之函数柯里化Currying
JavaScript专题之函数柯里化

js bind函数理解示例(使用了柯里化)

// 全局变量
a = 1

// 求和
let add = function (b, c) {
  return this.a + b + c
}

{
  /* 作为普通函数直接调用 */
  // 此时this指向window,window.a是1
  console.log(add(2, 3))// 6
}

{
  /* 简单版bind实现,不具备curry化效果(不支持在传入对象的同时传入其他参数) */
  Function.prototype.bind = function (obj) {
    // 缓存Function构造函数的this(表达式函数声明时相当于new Function...)
    const self = this
    // 闭包
    return function () {
      // 调用
      return self.apply(obj, arguments)
    }
  }

  let f1 = add.bind({ a: 0 })
  console.log(f1(2, 3))// 5

  // 但是简单版bind实现不支持对象和其他参数同时传入,如下:
  let f2 = add.bind({ a: 0 }, 2, 3)
  // 因为上一行代码传入的2、3并没有接收到,所以求和后返回NaN
  console.log(f2())// NaN
}

{
  /* 复杂版bind实现,具备部分curry化效果(之所以说部分,是因为它支持在传入对象的同时可以其他参数,但是不支持多次调用) */
  Function.prototype.bind = function (obj) {
    // 缓存Function构造函数的this(表达式函数声明时相当于new Function...)
    const self = this
    // 缓存除函数fn之外的所有参数
    let args = Array.prototype.slice.call(arguments, 1)
    // 闭包
    return function () {
      // 连接已缓存的老的参数和新传入的参数(即把每次传入的参数全部先保存下来,但是并不执行)
      let newArgs = args.concat(Array.from(arguments))
      // 调用
      return self.apply(obj, newArgs)
    }
  }

  let f1 = add.bind({ a: 0 })
  console.log(f1(2, 3))// 5

  // 复杂版bind实现支持对象和其他参数同时传入,如下:
  let f2 = add.bind({ a: 0 }, 2, 3)
  console.log(f2())// 5

  // 但是复杂版bind实现多次调用会报错,如下:
  let f3 = add.bind({ a: 0 })
  // console.log(f3(2)(3))// Uncaught TypeError: f3(...) is not a function
}

JS bind() 方法之人和狗的故事
JS中的bind的实现以及使用

装饰者模式理解示例(使用了柯里化)

大家看以下代码,是不是思路都是想通的,详细的大家就自行拓展吧

// 在之前执行
Function.prototype.before = function (fn) {
  let self = this
  return function () {
    fn.apply(this, arguments)
    return self.apply(this, arguments)
  }
}
// 在之后执行
Function.prototype.after = function (fn) {
  let self = this
  return function () {
    let res = self.apply(this, arguments)
    fn.apply(this, arguments)
    return res
  }
}
function foo(params) {
  console.log(params, 1)
  return 22
}
// 钩子可以共享参数和返回值
console.log(foo.before((params) => {
  console.log(params, 2)
}).after((params) => {
  console.log(params, 3)
})({ a: 1 }))

你可能感兴趣的:(原生javascript)