什么是函数柯里化
下文部分来自黑马程序员
函数柯里化 (Currying) 是一种将多个参数的函数转换为单个参数函数的技术。
转换完毕之后的函数:只传递函数的一部分参数来调用,让他返回一个新的函数去处理剩下的参数。
例子:
// 调整函数 sum
function sum(num1, num2) {
return num1 + num2
}
// 改写为 可以实现如下效果
console.log(sum(1)(2))
核心步骤:
sum
改为接收一个参数,返回一个新函数- 新函数内部将参数1,参数2累加并返回
function sum(num1) {
return function (num2) {
return num1 + num2
}
}
面试回答:
什么是函数柯里化
- 函数柯里化是一种将多个参数的函数转换为单个参数函数的技术
- 转换完毕之后的函数只需要传递一部分参数进行调用,并且会返回一个新的函数去处理剩下的参数
柯里化面试题-全局变量
柯里化在面试的时候一般以笔试题出现,比如
需求:
function sum(a, b, c, d, e) {
return a + b + c + d + e
}
// 改写函数sum实现:参数传递到5个即可实现累加
// sum(1)(2)(3)(4)(5)
// sum(1)(2,3)(4)(5)
// sum(1)(2,3,4)(5)
// sum(1)(2,3)(4,5)
核心步骤:
-
接收不定长参数
-
存储已传递的参数
-
判断长度
- 满足5:累加
- 不满足:继续返回函数本身
let nums = []
function currySum(...args) {
nums.push(...args)
if (nums.length >= 5) {
return nums.reduce((prev, curv) => prev + curv, 0)
} else {
return currySum
}
}
面试回答:
柯里化面试题-全局变量
-
定义数组保存参数
-
函数接收不定长参数
-
调用时将传入的参数,添加到数组中,并判断数组长度:
- 满足长度要求:累加并返回结果
- 未达到长度要求:继续返回函数本身
柯里化面试题-使用闭包
需求:
- 使用闭包将上一节代码中的全局变量,保护起来
- 支持自定义累加的参数个数
function sumMaker(length){
// 逻辑略
}
// 支持5个累加
const sum5 = sumMaker(5)
// 支持7个累加
const sum7 = sumMaker(7)
sum7(1,2,3)(4,5,6,7)
核心步骤:
-
定义外层函数:
- 定义参数
length
- 将全局变量迁移到函数内
- 定义参数
-
定义内层函数:
- 参数长度判断,使用传入的参数
length
- 直接复用上一节的逻辑,并返回
- 参数长度判断,使用传入的参数
function sumMaker(length) {
let nums = []
function inner(...args) {
nums.push(...args)
if (nums.length >= length) {
return nums.reduce((prev, curv) => prev + curv, 0)
} else {
return inner
}
}
return inner
}
面试回答:
柯里化面试题-使用闭包
- 定义函数,接收参数,用来确定参数个数
- 内部将上一节的逻辑拷贝进去
- 返回原函数
- 通过这样的调整,可以让我们自定义参数的个数,并且没有上一节的全局变量数组
柯里化实际应用-类型判断
通过参数复用,实现一个类型判断生成器函数
需求:
- 将下列4个类型判断函数,改写为通过函数
typeOfTest
动态生成
// 有如下4个函数
function isUndefined(thing) {
return typeof thing === 'undefined'
}
function isNumber(thing) {
return typeof thing === 'number'
}
function isString(thing) {
return typeof thing === 'string'
}
function isFunction(thing) {
return typeof thing === 'function'
}
// 改为通过 typeOfTest 生成:
const typeOfTest =function(){
// 参数 和 逻辑略
}
const isUndefined = typeOfTest('undefined')
const isNumber = typeOfTest('number')
const isString = typeOfTest('string')
const isFunction = typeOfTest('function')
// 可以通过 isUndefined,isNumber,isString,isFunction 来判断类型:
isUndefined(undefined) // true
isNumber('123') // false
isString('memeda') // true
isFunction(() => { }) // true
核心步骤:
typeOfTest
接收参数type
用来接收判断的类型- 内部返回新函数,接收需要判断的值,并基于
type
进行判断 - 使用箭头函数改写为最简形式~~传送门-github
const typeOfTest = (type) => {
return (thing) => {
return typeof thing === type
}
}
面试回答:
柯里化实际应用-类型判断
-
定义函数,接收需要判断的类型名
-
内部返回一个新的函数,
- 新函数接收需要判断的具体的值
- 新函数内部根据外层函数传入的类型,以及传入的值进行判断并返回结果
柯里化实际应用-固定参数
依旧是一个参数复用的实际应用
需求:
- 将如下3个请求的函数(都是post请求),变为通过
axiosPost
函数动态生成 - 实现函数
axiosPost
// 项目开发中不少请求的 请求方法 是相同的,比如
axios({
url: 'url',
method: 'get'
})
axios({
url: 'url',
method: 'get',
params: {
//
}
})
axios({
url: 'url',
method: 'post',
data: ''
})
axios({
url: 'url',
method: 'post',
data: '',
headers: {
}
})
// 固定请求参数,请求方法固定,其他参数从外部传递进来
// 需求: 实现方法requestWithMethod 支持如下调用
requestWithMethod('get')({
url: '',
params: {},
headers: {}
})
requestWithMethod('post')({
url: '',
headers: {},
data: {}
})
核心步骤:
-
函数内部固定请求方法,post
-
函数内部调用
axios
发请求即可 -
axios
内部就是这样实现的
function requestWithMethod(method) {
return (config) => {
return axios({
method,
...config
})
}
}
面试回答:
柯里化实际应用-固定参数
-
函数柯里化是一种函数式编程思想:将多个参数的函数转换为单个参数函数,调用时返回新的函数接收剩余参数
-
常见面试题,将函数改写为如下调用新式:核心思想就是返回新的函数,根据已经记录的参数长度判断:
- 长度符合要求:累加
- 长度不符合要求:继续返回
function sum(a, b, c, d, e) {
return a + b + c + d + e
}
// 改写函数sum实现:参数传递到5个即可实现累加
// sum(1)(2)(3)(4)(5)
// sum(1)(2,3)(4)(5)
// sum(1)(2,3,4)(5)
// sum(1)(2,3)(4,5)
-
常见应用:固定参数,比如
axios
中的:- 类型判断函数
- get,post,put等别名方法
- 就用到了柯里化的思想
封装一个柯里化函数
// 柯里化函数
function carry() {
// 获取第一个参数,即函数
let fn = arguments[0];
// 获取剩余参数
let args = Array.prototype.slice.call(arguments, 1);
// 判断参数是否已经全部传入
// fn.length 获取函数的参数长度
if (args.length === fn.length) {
// 如果已经全部传入参数,则直接执行函数
return fn.apply(this, args)
}
// 返回一个函数,该函数接收剩余参数
function _curry() {
// 将传入的参数与之前传入的参数合并
args.push(...arguments);
// 判断参数是否已经全部传入
if (args.length === fn.length) {
return fn.apply(this, args)
}
// 递归调用 curry 函数,继续接收参数
return _curry
}
return _curry
}
function add(a, b, c) {
return a + b + c
}
console.log(carry(add, 1, 2, 3)) // 6
console.log(carry(add, 1)(2, 3)) // 6
console.log(carry(add, 1)(2)(3)) // 6
console.log(carry(add)(1, 2, 3)) // 6
一道经典面试题
// add(1)(2)(3)
// add(1, 2, 3)(4)
// add(1)(2)(3)(4)(5)
// 根据以上代码,实现一个add函数,要求使用函数柯里化
function add() {
// 将传入的所有参数转换为数组
let args = [...arguments]
// 定义一个函数,用于接收新的参数,并返回一个新的函数
function calculator() {
// 将传入的新参数转换为数组
args.push(...arguments)
return calculator
}
// 在calculator函数中定义toString方法,用于返回计算结果
calculator.toString = function () {
// 将数组中的所有参数相加并返回结果
return args.reduce((a, b) => a + b)
}
// 返回calculator函数,以便可以继续调用add函数进行计算
return calculator
}
console.log(add(1)(2)(3).toString())
console.log(add(1, 2, 3)(4).toString())
console.log(add(1)(2)(3)(4)(5).toString())