简述 JavaScript 中的高阶函数和函数柯里化

165 阅读2分钟

1.什么是高阶函数

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入
  • 输出一个函数

利用高阶函数可以解决什么问题

  1. 扩展方法

如:

function say (args) {    // 我们需要对say方法进行扩展,但是不能修改源码
  console.log('say', args)
}
Function.prototype.before = function (cb) {
  return (...args) => {   // newSay
    cb()
    this(...args)   // 扩展原来的函数
  }
}

let newSay = say.before(() => {
  console.log('before')
})
newSay('hello')

// 输出
// before
// say hello

我们可以通过高阶函数来实现参数的保留

示例:

比如我们可以实现判断值的类型

一般判断数据类型有以下方法

  • typeof 一般用于判断基本类型
  • instanceof xxx是谁的实例,原理
  • Object.prototype.toString.call 判断具体类型,返回的是一个字符串
  • constructor可以看当前实例是由谁构造出来的, 深拷贝

我们先定义一个通用的判定函数

function isType(val, typing) {
  return Object.prototype.toString.call(val) === `[object ${typing}]`
}

那么我们判断一个值是否为字符串可以这样写

console.log(isType('hello', 'String'))		// true
console.log(isType(456, 'String'))	// false

那么我们能不能定义isStringisNumber函数,不需要用户手动传入 StringNumber字段呢?当然可以,下面改造isType函数

function isType (typing) {
  return (val) => {
    return Object.prototype.toString.call(val) === `[object ${typing}]`
  }
}
// 比较函数声明的作用域和执行的作用域不一样的,这时候就会形成闭包
let isString = isType('String')
console.log(isString('123'))	// true
console.log(isString(123))	// false

利用高阶函数保留参数,进一步可以实现函数的柯里化和函数的反柯里化

2.什么是柯里化

柯里化(currying)指的是将一个多参数的函数拆分成一系列函数,每个拆分后的函数都只接受一个参数(unary)。

下面我们尝试实现通用的柯里化参数,也是一个高阶函数

function sum (a, b, c, d) {
  return a + b + c + d
}
sum(1, 2, 3, 4) // 10

上面代码中,函数sum接受四个参数abcd

柯里化就是将上面的函数拆分成四个函数,每个函数都只接受一个参数。

function curring (fn) {
  // 存储每次调用的时候传入的变量
  const inner = (args = []) => {
    return args.length >= fn.length ? fn(...args) : (...userArgs) => inner([...args, ...userArgs])// 递归返回函数
  }
  return inner()
}
let sum1 = curring(sum)
let sum2 = sum1(1)
let sum3 = sum2(2, 3)
let result = sum3(4)
console.log(sum4)

然后再来看我们的类型判断函数

let isString = curring(isType)('String')
console.log(isString('abc'))		// true
let isNumber = curring(isType)('Number')
console.log(isNumber(123))		// true

如果有很多很多判断变量的方法,我们可以整合一下

let util = {};
['String', 'Number', 'Boolean', 'Null', 'Undefined'].forEach(type => {
  util[`is${type}`] = curring(isType)(type)
});
console.log(util.isString(123))	// false
console.log(util.isString('123'))	// true
console.log(util.isNumber(123))	// true
console.log(util.isBoolean(123))	// false
console.log(util.isBoolean(true))	// true
console.log(util.isNull(null))	// true
console.log(util.isNull(123))	// false
console.log(util.isUndefined(undefined))	// true
console.log(util.isUndefined(null))	// false

我们用3行代码就实现了几乎JavaScript的所有数据类型判断,是不是很厉害!