apply(context,[arguments])
,call(context,param1,param2,...)
。柯里化(currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
在这里举个例子,有一个add()
函数,它是用来处理我们传给它的参数(param1,params2,…)相加求和的一个函数。
// 在这里第一个具有两个参数`x`、`y`的`add(x , y)`函数function add(x , y){return x + y;}// 调用`add()`函数,并给定两个参数`4`和`6`add(4,6);// 模拟计算机操作,第一步 传入第一个参数 4function add(4 , y){return 4 + y;}// 模拟计算机操作,第二步 传入第一个参数 6function add(4 , 6){return 4 + 6;}
如果我们将add()
函数柯里化,是什么样子呢?在这里简单的实现一下:
// 柯里化过的add()函数,可以接受部分参数function add(x ,y){if (typeof y === 'undefined') {return function (newy){return x + newy;}}// 完整应用return x + y;}// 测试调用console.l整数是什么og(typeof add(4)); // [function]console.log(add(4)(6)); // 10// 可以创建保存函数let saveadd = add(4);console.log(saveadd(6)); // 10
从以上简单柯里化的add()
函数可以看出,函数可以接受部分函数,然后返回一个新的函数,使其继续处理剩下的函数。
在这里我们创建一个公共的柯里化函数,那样我们就不必每次写一个函数都要在其内部实现复杂的柯里化过程。
// 定义一个createcurry的函数function createcurry(fn){var slice = array.prototype.slice,stored_args = slice.call(arguments,1);return function () {let new_args = slice.call(arguments),args = stored_args.concat(new_args);return fn.apply(null,args);}}
在以上公共的柯里化函数中:
arguments
,并不是一个真的数组,只是一个具有length
属性的对象,所以我们从array.prototype
中借用slice
方法帮我们把arguments
转为一个真正的数组,方便我们更好的操作。当我们第一次调用函数createcurry
的时候,其中变量stored_args
是保持了除去第一个参数以外的参数,因为第一个参数是我们需要柯里化的函数。当我们执行createcurry
函数中返回的函数时,变量new_args
获取参数并转为数组。内部返回的函数通过闭包访问变量stored_args
中存储的值和变量new_args
的值合并为一个新的数组,并赋值给变量args
。最后调用fn.apply(null,args)
方法,执行被柯里化的函数。现在我们来测试公共的柯里化函数
// 普通函数add()function add(x , y){return x + y;}// 柯里化得到一个新的函数var newadd = createcurry(add,4);console.log(newadd(6)); // 10//另一种简便方式console.log(createcurry(add,4)(6));// 10
当然这里并不局限于两个参数的柯里化,也可以多个参数:
// 多个参数的普通函数function add(a,b,c,d){return a + b + c + d;}// 柯里化函数得到新函数,多个参数可以随意分割console.log(createcurry(add,4,5)(5,6)); // 20// 两步柯里化let add_one = createcurry(add,5);console.log(add_one(5,5,5));// 20let add_two = createcurry(add_one,4,6);console.log(add_two(6)); // 21
通过以上的例子,我们可以发现一个局限,那就是不管是两个参数还是多个参数,它只能分两步执行,如以下公式:
fn(x,y) ==> fn(x)(y);fn(x,y,z,w) ==> fn(x)(y,z,w) || fn(x,y)(z,w)||…如果我们想更灵活一点:
fn(x,y) ==> fn(x)(y);fn(x,y,z) ==> fn(x,y)(z) || fn(x)(y)(z);fn(x,y,z,w) ==> fn(x,y)(z)(w) || fn(x)(y)(z)(w) || …;我们该怎么实现呢?
经过以上练习,我们发现我们创建的柯里化函数存在一定局限性,我们希望函数可以分为多步执行:
// 创建一个可以多步执行的柯里化函数,当参数满足数量时就去执行它:// 函数公式:fn(x,y,z,w) ==> fn(x)(y)(z)(w);let createcurry = (fn,...params)=> {let args = parsms || [];let fnlen = fn.length; // 指定柯里化函数的参数长度return (...res)=> {// 通过作用域链获取上一次的所有参数let allargs = args.slice(0);// 深度拷贝闭包共用的args参数,避免后续操作影响(引用类型)allargs.push(...res);if(allargs.length < fnlen){ // 当参数数量小于原函数的参数长度时,递归调用createcurry函数 return createcurry.call(this,fn,...allargs);}el{ // 当参数数量满足时,触发函数执行 return fn.apply(this,allargs);}}}// 多个参数的普通函数function add(a,b,c,d){水的循环过程return a + b + c + d;}// 测试柯里化函数let curryadd = createcurry(add,1);console.log(curryadd(2)(3)(4)); // 10
以上我们已经实现了灵活的柯里化函数,但是这里我们又发现了一个问题:
如果我第一次就把参数全部传入,但是它并没有返回结果,而是一个函数(function)。只有我们再次将返回的函数调用一次才能返回结果:curryadd(add,1,2,3,4)()
;可能有人说如果是全部传参,就调用原来的add()
函数就行了,这也是一种办法;但是我们在这里既然是满足参数数量,对于这种情况我们还是处理一下。在这里我们只需要在返回函数前做一下判断就行了:
let createcurry = (fn,...params)=> {let args = parsms || [];let fnlen = fn.length; // 指定柯里化函数的参数长度if(length === _args.length){ // 加入判断,如果第一次参数数量以经足够时就直接调用函数获取结果 return fn.apply(this,args); }return (...res)=> {let allargs = args.slice(0);allargs.push(...res);if(allargs.length < fnlen){ return createcurry.call(this,fn,...allargs);}el{ return fn.apply(this,解毒食物allargs);}}}
以上可以算是完成了一个灵活的柯里化的函数了,但是这里还不算很灵活,因为我们不能控制它什么时候执行,只要参数数量足够它就自动执行。我们希望实现一个可以控制它执行的时机该怎么办呢?
我们这里直接说明一下函数公式:
fn(a,b,c) ==> fn(a)(b)(c )();fn(a,b,c) ==> fn(a);fn(b);fn(c );fn();当我们参数足够时它并不会执行,只有我们再次调用一次函数它才会执行并返回结果。在这里我们在以上例子中加一个小小的条件就可以实现。// 当参数满足,再次执行时调用函数let createcurry = (fn,...params)=> {let args =彭佳慧 走在红毯那一天 parsms || [];let fnlen = fn.length; // 指定柯里化函数的参数长度//当然这里的判断需要注释掉,不然当它第一次参数数量足够时就直接执行结果了//if(length === _args.length){ // 加入判断,如果第一次参数数量以经足够时就直接调用函数获取结果 //return fn.apply(this,args); //}return (...res)=> {let allargs = args.slice(0);allargs.push(...res);// 在这里判断输入的参数是否大于0,如果大于0在判断参数数量是否足够,// 这里不能用 && ,如果用&& 也是参数数量足够时就执行结果了。if(res.length > 0 || allargs.length < fnlen){ return createcurry.call(this,fn,...allargs);}el{ return fn.apply(this,allargs);}}}// 多个参数的普通函数function add(a,b,c,d){return a + b + c + d;}// 测试可控制的柯里化函数let curryadd = createcurry(add,1);console.log(curryadd(2)(3)(4)); // functionconsole.log(curryadd(2)(3)(4)()); // 10console.log(curryadd(2)(3)()); // 当参数不足够时返回 nan
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注www.887551.com的更多内容!
本文发布于:2023-04-04 14:49:06,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/4adc64a670077d35200e5d6ba08a9151.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:JavaScript函数柯里化详解.doc
本文 PDF 下载地址:JavaScript函数柯里化详解.pdf
留言与评论(共有 0 条评论) |