js - 理解函数柯里化
Posted 耳东蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js - 理解函数柯里化相关的知识,希望对你有一定的参考价值。
怎么理解柯里化
柯里化的意思就是将一个多元函数,转换成一个依次调用的单元函数。
curry 的这种用途可以理解为:参数复用。本质上是降低通用性,提高适用性。
f(a,b,c) → f(a)(b)(c)
var add = function(x)
return function(y)
return x + y;
;
;
const increment = add(1);
increment(10); // 11
var curry = require('lodash')
var abc = function(a, b, c)
return [a, b, c];
;
var curried = curry(abc);
console.log(curried(1)(2)(3));
// => [1, 2, 3]
var person = [name: 'kevin', age: 12 , name: 'daisy', age: 13]
var prop = curry(function (key, obj)
return obj[key]
);
// 普通方式
var name = person.map(function (item)
return item.name;
)
// 柯里化方式
var name = person.map(prop('name'))
console.log(name) // [ 'kevin', 'daisy' ]
var age = person.map(prop('age'))
console.log(age) // [ 12, 13 ]
上面代码,我们将返回对象属性的部分通过函数编程实现。
var prop = curry(function (key, obj)
return obj[key]
);
curry函数将传入的function内的两个参数。变成两个使用一个参数的函数执行。
function(key, obj)
// 根据柯里化转换转换成两个函数,并且第一个参数调用之后返回的还是一个函数,参数为原函数的第二个参数。
props(key) => function(obj)
精髓理解:用闭包把参数保存起来,当参数的数量足够执行函数了,就开始执行函数
手写柯里化
初始版本
var abc = function(a, b, c)
return [a, b, c];
;
function customerCurry(fn)
const fnArgsCount = fn.length; // fn函数的形参个数
let curryArgs = []; // 柯里化函数已经传入的实惨个数
function wrapperFunction ()
curryArgs = curryArgs.concat([...arguments])
if (curryArgs.length < fnArgsCount)
return wrapperFunction;
return fn.apply(this, curryArgs)
return wrapperFunction;
var incre = customerCurry(abc)
incre(1)(2)(3) // 正常返回 [1, 2, 3]
incre() // 返回 [1, 2, 3]
上面的代码可以正常返回,但是还是存在问题。
只能执行一次,incre不能多次执行。这里因为上面书写的方式将参数统一记录下来了。customerCurry参数是最外层必包在维护。所以在incre(1)(2)(3) 执行完之后。内部参数个数已经达到三个。
第二版本
var abc = function(a, b, c)
return [a, b, c];
;
function customerCurry(fn)
const fnArgsCount = fn.length; // fn函数的形参个数
let curryArgs = []; // 柯里化函数已经传入的实惨个数
if (fnArgsCount <= 1)
throw new Error('函数至少传入两个参数');
function wrapperFunction ()
curryArgs = [...arguments];
if (curryArgs.length < fnArgsCount)
return eval(`wrapperFunction.bind(this, $curryArgs.reduce(function(x, y) return x + ',' + JSON.stringify(y);) )`);
return fn.apply(this, curryArgs)
return wrapperFunction;
var incre = customerCurry(abc)
incre(1)(2)(3) // 正常返回 [1, 2, 3]
当然这种还是有缺陷,因为对于参数的传递,这里处理不是很好,使用的是json的方法。
第三版本
var abc = function(a, b, c)
return [a, b, c];
;
function customerCurry(fn)
const fnArgsCount = fn.length; // fn函数的形参个数
const curryWrapperArgs = [...arguments].slice(1);
function wrapperFunction ()
let curryArgs = [fn, ...curryWrapperArgs, ...arguments];
if (curryArgs.length < fnArgsCount + 1)
return customerCurry.apply(this, curryArgs);
return fn.apply(this, curryArgs.slice(1))
return wrapperFunction;
var incre = customerCurry(abc)
incre(1)(2)(3) // 正常返回 [1, 2, 3]
第四版本
这样写起来没什么问题,但是既然学到了柯里化,函数式编程的思想去书写一下。
例子来源:https://github.com/mqyqingfeng/Blog/issues/42
// 第二版
function sub_curry(fn)
var args = [].slice.call(arguments, 1);
return function()
return fn.apply(this, args.concat([].slice.call(arguments)));
;
function curry(fn, length)
length = length || fn.length;
var slice = Array.prototype.slice;
return function()
if (arguments.length < length)
var combined = [fn].concat(slice.call(arguments));
return curry(sub_curry.apply(this, combined), length - arguments.length);
else
return fn.apply(this, arguments);
;
执行顺序:
换一个方向:
现在我们用函数式编程的思想来理解一下。
第五版本:高颜值写法
var curry = fn =>
judge = (...args) =>
args.length === fn.length
? fn(...args)
: (arg) => judge(...args, arg)
参照
- https://github.com/mqyqingfeng/Blog/issues/42
- https://juejin.cn/post/6844903936378273799#heading-14
以上是关于js - 理解函数柯里化的主要内容,如果未能解决你的问题,请参考以下文章