手写柯里化累加器

手写柯里化累加器

create by db on 2021-5-27 18:26:30
Recently revised in 2021-5-27 18:26:34

闲时要有吃紧的心思,忙时要有悠闲的趣味

目录

前言

返回目录

 面试必问,不说废话。

正文

一、柯里化(Currying)

返回目录

 柯里化(Currying)是把接受多个参数的函数转变为单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。

解释

  • 通过闭包管理

  • 支持链式调用

  • 每次运行返回一个 function

    即:通过将多个参数换成一个参数,每次运行返回新函数的技术

举例

// 普通的add函数
function add(x, y) {
  return x + y
}

// Currying后
function curryingAdd(x) {
  return function (y) {
    return x + y
  }
}

add(1, 2) // 3
curryingAdd(1)(2) // 3。
复制代码

应用场景

1. 参数复用

 在一个正则的校验中,正常来说直接调用 check 函数就可以了,但是如果我有很多地方都要校验是否有数字,其实就是需要将第一个参数 reg 进行复用,这样别的地方就能够直接调用 hasNumberhasLetter 等函数,让参数能够复用,调用起来也更方便。

// 正常正则验证字符串 reg.test(txt)

// 函数封装
function check(reg, txt) {
  return reg.test(txt)
}

// 校验数字
check(/\d+/g, 'test') // false
// 校验字母
check(/[a-z]+/g, 'test') // true

// Currying后
function curryingCheck(reg) {
  return function (txt) {
    return reg.test(txt)
  }
}
// 校验数字
var hasNumber = curryingCheck(/\d+/g)
// 校验字母
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
复制代码

2. 返回函数,延迟运行

 像我们 js 中经常使用的 bind,实现的机制就是 Currying.

Function.prototype.bind = function (context) {
  var _this = this
  var args = Array.prototype.slice.call(arguments, 1)
  return function () {
    return _this.apply(context, args)
  }
}
复制代码

二、柯里化累加器

 经典面试题,柯里化累加器

// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
复制代码

1. 确定参数的函数柯里化实现

// 累加函数,只能传四个参数
function add(a, b, c, d) {
  return a + b + c + d
}
// 柯里化函数
function curry(fn) {
  return function add(...args) {
    if (args.length < fn.length) {
      // 判断接受的参数是否小于函数的参数长度
      return function () {
        // 参数不够长度,再次接受传递参数
        return add(...args, ...arguments)
      }
    }
    return fn(...args) // 不要求改变this
  }
}
let curried = curry(add)

console.log(curried(1)(2)(3)(4)) //10
console.log(curried(1, 2)(2, 4)) //9
复制代码

2.不确定参数实现无限累加

// 累加函数,参数不限
function add(arr) {
  return arr.reduce((acc, item) => {
    return acc + item
  })
}
// 柯里化函数
function curry(fn) {
  let parmas = []
  return function add(...args) {
    if (args.length) {
      //判断是否有参数
      parmas = [...parmas, ...args]
      return add
    }
    return fn(parmas)
  }
}

let curried = curry(add)
console.log(curried(1)(2)(3)(4)(10, 20)()) //40
// 注意最后的调用用方式,()调用不传递参数,会跳出判断,调用累加函数
复制代码

3.一个函数实现不确定参数无限累加

function add() {
  // 第一次执行时,定义一个数组专门用来存储所有的参数
  const argList = Array.from(arguments)

  // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
  const calculate = function () {
    argList.push(...arguments)
    return calculate
  }

  // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
  calculate.toString = function () {
    return argList.reduce((a, b) => a + b, 0)
  }

  return calculate
}

// 实现一个 add 方法,使计算结果能够满足以下预期
console.log(add(1)(2)(3)) // 6
console.log(add(1, 2, 3)(4)) // 10;
console.log(add(1)(2)(3)(4)(5)) // 15;

var a = add(1)(2)(3)(4) // f 10
var b = add(1, 2, 3, 4) // f 10
var c = add(1, 2)(3, 4) // f 10
var d = add(1, 2, 3)(4) // f 10

// 可以利用隐式转换的特性参与计算
console.log(a + 10) // 20
console.log(b + 20) // 30
console.log(c + 30) // 40
console.log(d + 40) // 50

// 也可以继续传入参数,得到的结果再次利用隐式转换参与计算
console.log(a(10) + 100) // 120
console.log(b(10) + 100) // 120
console.log(c(10) + 100) // 120
console.log(d(10) + 100) // 120
复制代码

Tips

 要补充的知识点是函数的隐式转换。当我们直接将函数参与其他的计算时,函数会默认调用 toString 方法,直接将函数体转换为字符串参与计算。

function fn() {
  return 20
}
console.log(fn + 10) // 输出结果 function fn() { return 20 }10
复制代码

 而为了打印出我们想要的结果我们就需要自己重写 toString 方法

function fn() {
  return 20
}
fn.toString = function () {
  return 30
}

console.log(fn + 10) // 40
复制代码

 除此之外,当我们重写函数的 valueOf 方法也能够改变函数的隐式转换结果。

function fn() {
  return 20
}
fn.valueOf = function () {
  return 60
}

console.log(fn + 10) // 70
复制代码

 当我们同时重写函数的 toString 方法与 valueOf 方法时,最终的结果会取 valueOf 方法的返回结果。

function fn() {
  return 20
}
fn.valueOf = function () {
  return 50
}
fn.toString = function () {
  return 30
}

console.log(fn + 10) // 60
复制代码

总结

返回目录

 路漫漫其修远兮,与诸君共勉。

参考文献

后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址

文档协议

知识共享许可协议
db 的文档库db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享