定义
柯里化(英语:Currying) 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
来源: 维基百科
说白了就是返回函数的函数,其实在开发过程中多半已经用过了,只是之前没有去了解过这个东西叫什么。
我认为,他还可以是被看为一种轻量级的面向对象。
示例
定义一个getAdder
函数,返回一个adder
:
1 2 3 4 5 6 7 8
| function getAdder(n) { return m => n + m }
const adder = getAdder(3)
console.log(adder(4))
|
这便是一种柯里化的应用了。
实际问题
我在开发过程中遇到过这种需求,也就是你现在看到的页面,它是用markdown写成的,并且文件头部有一些元信息,即metadata
,本质是一堆键值对,用来描述这个文件。比如当前文件的头部就是这样的:
1 2 3 4 5
| date: 2022-5-14 category: Learn tags: JS TS intro: 返回函数的函数,可以认为是轻量级面向对象 id: 11
|
我把这个结构抽象一下,改成下面这样:
我们的需求就是读取出来这些键值对。
解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function parser(metadata) { return key => { const prefix = key + ':' const data = metadata.find(s => s.startsWith(prefix))
return data.substring(prefix.length).trim() } }
const parse = parser([ 'foo: abc', 'bar: def' ])
console.log(parse('foo'))
console.log(parse('bar'))
|
可以看出,这里的parser
函数就是保存了metadata
这一变量内容并且重复使用。
针对于parser
函数的执行效率,可以进行以下优化:
1 2 3 4 5 6 7 8
| function parser(metadata) { const map = new Map() for (const data of metadata) { const [key, value] = data.split(/\s*:\s*/) map.set(key, value) } return key => map.get(key) }
|
这里提前把所有的键值对都保存下来了,到执行的时候只需要获取一下即可。
那么回到开头说的,他可以被看成一种轻量级的面向对象,所以说完全可以用到面向对象的方法实现这个功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Parser { metadata = new Map()
constructor(metadata) { for (const data of metadata) { const [key, value] = data.split(/\s*:\s*/) this.metadata.set(key, value) } }
parse(key) { return this.metadata.get(key) } }
const metadata = [ 'foo: abc', 'bar: def' ]
const parser = new Parser(metadata)
console.log(parser.parse('foo'))
console.log(parser.parse('bar'))
|
但是实际应用中还是通过柯里化的方法更简洁明了,这也正体现这JS这门语言的魅力: 提供多种解决手段,我们总能从中找到较优的解决方案。