javascript实现函数柯里化与反柯里化过程解析_javascript技巧

来源:脚本之家  责任编辑:小易  

会调就行了,要想懂你找javascript的源码看看www.zgxue.com防采集请勿采集本网。

函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感;下面来一起看看究竟什么是函数柯里化:

function fibonacci(n){ if(n=0)return 0;if(n=1)return 1;return fibonacci(n-1)+fibonacci(n-2);}

维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术。其由数学家Haskell Brooks Curry提出,并以curry命名。

您是怎样调用?我分别是过这两个方法都得哦。您试试。方法1:这个是通过一进入网页调用的。charset=gb2312\"> 新建网页 1 javascript\"> function ChangeQty(pn) { window.

概念往往都是干涩且难懂的,让我们用人话来解释就是:如果我们不确定这个函数有多少个参数,我们可以先给它传入一个参数,然后通过JS闭包(如若不懂JS闭包,请先学习闭包知识点再来学习本篇博文https://www.zgxue.com/article/171398.htm)来进行返回一个函数,内部函数接收除开第一个参数外的其余参数进行操作并输出,这个就是函数的柯里化;

js内置函数是浏览器内核自带的,不用任何函数库引入就可以直接使用的函数。javascript内置函数一共可分为五类: 1、常规函数 2、数组函数 3、日期函数 4、数学函数 5、字符串函数 第一类:常规

举个小例子:

就是间接的调用了splice和concat,slice函数了,push可见操作性极高的,效率不好了

场景(需求):

document.body.scrollTop=document.body.scrollHeight;这样就可以。你的代码错就错在window.document.body.scrollTop=windows.document.body.scrollHeight;后一个windows多了个s!另外window对象

众所周知程序员每天加班的时间还是比较多的,如果我们需要计算一个程序员每天的加班时间,那么我们的第一反应应该是这样;

var overtime=0;function time(x){ return overtime+=x;}time(1); //1time(2); //3time(3); //6

上面的代码固然没有问题,可是需要每天调用都算加一下当天的时间,很麻烦,并且每调用一次函数都要进行一定的操作,如果数据量巨大,有可能会有影响性能的风险,那么有没有可以偷懒又能解决问题的办法呢?有的!

function time(x){ return function(y){ return x+y; } }var times=time(0);times(3);

但是上面代码依然存在问题,在实际开发中很多时候我们的参数是不确定的,上面代码虽然简单的实现了柯里化的基本操作,但是对于参数不确定的情况是处理不了的;所以存在着函数参数的局限性;不过我们从上面的代码中基本可以知道函数柯里化是个啥意思了;就是一个函数调用的时候只允许传入一个参数,然后通过闭包返回内部函数去处理和接收剩余参数,返回的函数通过闭包的方式记住了time的第一个参数;

我们再来把代码改造一下:

// 首先定义一个变量接收函数var overtime = (function() {//定义一个数组用来接收参数 var args = [];//这里运用闭包,调用外部函数返回一个内部函数 return function() {  //arguments是浏览器内置对象,专门用来接收参数  //如果参数的长度为0即没有参数的时候 if(arguments.length === 0) {    //定义变量用来累加 var time = 0;    //循环累加,用i和args的长度进行比较 for (var i = 0, l = args.length; i < l; i++) {    //进行累加操作 等价于time=time+args[i] time += args[i]; }    // 返回累加的结果 return time;    //如果arguments对象参数长度不为零,即有参数的时候 }else {    //定义的空数组添加arguments参数作为数组项,第一个参数古args作为改变this指向,第二个参数arguments把剩余参数作为数组形式添加至空数组中 [].push.apply(args, arguments); } }})();overtime(3.5); // 第一天overtime(4.5); // 第二天overtime(2.1); // 第三天//...console.log( overtime() ); // 10.1

代码经过我们的改造已经实现了功能,但是这不是一个函数柯里化的完整实现,那么我们要怎么完整实现呢?下面我们来介绍一种通用的实现方式:

通用的实现方式:

//定义方法currying,先传入一个参数var currying=function(fn){  //定义空数组装arguments对象的剩余参数 var args=[];  //利用闭包返回一个函数处理剩余参数 return function (){    //如果arguments的参数长度为0,即没有剩余参数 if(arguments.length===0){    //执行上面方法    //这里的this指向下面的s,类似于s(),代表参数长度为0的时候直接调用函数 return fn.apply(this,args) } console.log(arguments)  //如果arguments的参数长度不为0,即还有剩余参数  //在数组的原型对象上添加数组,apply用来更改this的指向为args  //将[].slice.call(arguments)的数组添加到原型数组上  //这里的[].slice.call(arguments)===Array.prototype.slice.call(arguments)实质上就是将arguments对象转成数组并具有slice功能 Array.prototype.push.apply(args,[].slice.call(arguments)) //args.push([].slice.call(arguments)) console.log(args)  //这里返回的arguments.callee是返回的闭包函数,callee是arguments对象里面的一个属性,用于返回正被执行的function对象 return arguments.callee }}  //这里调用currying方法并传入add函数,结果会返回闭包内部函数 var s=currying(add);  //调用闭包内部函数,当有参数的时候会将参数逐步添加到args数组中,待没有参数传入的时候直接调用  //调用的时候支持链式操作 s(1)(2)(3)();//也可以一次性传入多个参数 s(1,2,3); console.log(s());

JS函数柯里化的优点:

1.可以延迟计算,即如果调用柯里化函数传入参数是不调用的,会将参数添加到数组中存储,等到没有参数传入的时候进行调用;

2.参数复用,当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。

世间万物相对,有因必有果,当然了,有柯里化必然有反柯里化;

反柯里化(uncurrying)

从字面意思上来讲就是跟柯里化的意思相反;其实真正的反柯里化的作用是扩大适用范围,就是说当我们调用某个方法的时候,不需要考虑这个对象自身在设计的过程中有没有这个方法,只要这个方法适用于它,我们就可以使用;(这里引用的是动态语言中的鸭子类型的思想)

在学习JS反柯里化之前,我们先学习一下动态语言的鸭子类型思想,以助于我们更好的理解:

动态语言鸭子类型思想(维基百科解释):

在程序设计中,鸭子类型(duck typing)是动态类型的一种风格。

在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。

这个概念的名字来源于由 James Whitcomb Riley 提出的鸭子测试,“鸭子测试”可以这样表述:

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

理论上的解释往往干涩难懂,换成人话来说就是:你是你妈妈的儿子/女儿,不管你是否优秀,是否漂亮,只要你是你妈亲生的,那么你就是你妈的儿子/女儿;换成鸭子类型就是,只要你会呱呱叫,走起来像鸭子,只要你拥有的行为像鸭子,不管你是不是鸭子,那么你就可以被称为鸭子;

在Javascript中有很多鸭子类型的引用,比如我们在对一个变量进行赋值的时候,显然是不需要考虑变量的类型的,正是因为如此,Javascript才更加的灵活,所以Javascript是一门典型的动态类型语言;

我们来看一下反柯里化中是怎么引用鸭子类型的:

//函数原型对象上添加uncurring方法Function.prototype.uncurring = function() {//改变this的指向 //这里的this指向是Array.prototype.push var self = this; //这里的闭包用来返回内部函数的执行 return function() { //创建一个变量,在数组的原型对象上添加shift上面删除第一个参数 //改变数组this的指向为arguments var obj = Array.prototype.shift.call(arguments); //最后返回执行并给方法改变指向为obj也就是arguments // 并传入arguments作为参数 return self.apply(obj, arguments); };};//数组原型对象上添加uncurrying方法var push = Array.prototype.push.uncurring();//测试一下//匿名函数自执行(function() { //这里的push就是一个函数方法了 //相当于传入参数arguments和4两个参数,但是在上面shift方法中删除第一个参数,这里的arguments参数被截取了,所以最后实际上只传入了4 push(arguments, 4); console.log(arguments); //[1, 2, 3, 4]//匿名函数自调用并带入参数1,2,3})(1, 2, 3)

到这里大家可以想一想arguments是一个接收参数的对象,里面是没有push方法的,那么arguments为什么能调用push方法呢?

这是因为代码var push = Array.prototype.push.uncurring();在数组的原型对象的push方法上添加了uncurring方法,然后在执行匿名函数的方法push(arguments, 4);时候实质上是在调用上面的方法在Function的原型对象上添加uncurring方法并返回一个闭包内部函数执行,在执行的过程中因为Array原型对象上的shift方法会把 push(arguments, 4);中的arguments截取,所以其实方法的实际调用是push(4),所以最终的结果才是[1,2,3,4]

在《JavaScript设计模式与开发实践》一书中,JS函数的反柯里化的案例是这样写的:

//定义一个对象var obj = { "length":1, "0":1}//在Function原型对象定义方法uncurryingFunction.prototype.uncurrying = function() { //this指向Array.prototype.push var self = this; //闭包返回一个内部函数 return function() { // 这里可以拆开理解 //首先执行apply return //Function.prototype.call(Array.prototype.push[obj,2]) //然后Array.prototype.push.call(obj,2) //call改变指向 obj.push(2) //所以最后结果就是 {0: 1, 1: 2, length: 2} return Function.prototype.call.apply(self, arguments);}}//在var push = Array.prototype.push.uncurrying()push(obj, 2) console.log(obj);//{0: 1, 1: 2, length: 2}

上面的方式不好理解?没关系,咱们来个好理解的:

Function.prototype.unCurrying = function () { var self = this; return function () {    //[].slice.call(arguments,1)===Array.prototype.push.slice.call(arguments,1)===arguments.slice(1)return self.apply(arguments[0], [].slice.call(arguments, 1)); };};var push = Array.prototype.push.uncurrying()console.log(push);push(obj,2) //{0: 1, 1: 2, length: 2}console.log(obj);

分析一下:

1、首先在Function原型对象上添加uncurrying方法,这样所有的Function都可以借用;

2、返回一个闭包内部函数

3、闭包函数返回的结果中返回的是调用方法,self指向Array.prototype.push,apply方法中第一个参数是更改指向,对应下面push(obj,2)相当于更改指向为obj.push(2)

4、apply方法中第二个参数的call方法是更改指向为arguments,并且arguments中能使用slice方法,等于arguments.slice(1)

由于priceArray[i].value是字符串,所以你直接相加的话是按字符串连接来处理的,应该先把它转化为数字:totalPrice=totalPrice+parseInt(priceArray[i].value);其实标准的javascript写法是:totalPrice+parseInt(priceArray[i].value);内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • javascript函数柯里化原理与用法分析
  • 浅谈js中的bind方法与函数柯里化
  • javascript闭包与函数柯里化浅析
  • javascript函数柯里化详解
  • javascript偏函数与柯里化实例详解
  • 深入剖析javascript中的函数currying柯里化
  • javascript中利用柯里化函数实现bind方法【推荐】
  • javascript中利用柯里化函数实现bind方法
  • 深入解析javascript中函数的currying柯里化
  • js函数柯里化的方法和作用实例分析
  • js 组件系列之bootstrap table的冻结列功能彻底解决高度问题
  • bootstrap表单控件学习使用
  • javascript 拾漏补遗
  • dropify.js图片宽高自适应的方法
  • javascript new 需不需要继续使用
  • indexof 和 lastindexof 使用示例介绍
  • javascript中提前声明变量或函数例子
  • 原生js实现水平方向无缝滚动
  • layui的验证码功能及校验实例
  • 得到当前行的值的javascript代码
  • javascript实现两个数相加的函数是什么?
  • javascript 函数的内部实现问题 如isNaN()函数?
  • javascript 编写一个函数sum()用于求和。
  • javascript中函数和方法的区别
  • javascript实现一个函数,求大神提示?
  • javascript open函数
  • javascript内置函数是什么?
  • javascript 一段实现push函数的代码,看不懂,可以解释一下吗?
  • 怎么在javascript的函数里面实现跳转到页面底部
  • 请用JavaScript写一个format函数,实现如下需求:(
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全基础知识javascript类库表单特效广告代码网页特效黑客性质javascript技巧domnode.jsjs其它首页javascriptjavascript技巧javascript函数柯里化原理与用法分析浅谈js中的bind方法与函数柯里化javascript闭包与函数柯里化浅析javascript函数柯里化详解javascript偏函数与柯里化实例详解深入剖析javascript中的函数currying柯里化javascript中利用柯里化函数实现bind方法【推荐】javascript中利用柯里化函数实现bind方法深入解析javascript中函数的currying柯里化js函数柯里化的方法和作用实例分析js 组件系列之bootstrap table的冻结列功能彻底解决高度问题bootstrap表单控件学习使用javascript 拾漏补遗dropify.js图片宽高自适应的方法javascript new 需不需要继续使用javascript中提前声明变量或函数例子原生js实现水平方向无缝滚动layui的验证码功能及校验实例得到当前行的值的javascript代码js刷新页面方法大全js中settimeout()的用法详解js截取字符串常用方法详细整理js页面跳转常用的几种方式js打开新窗口的2种方式js数组与字符串的相互转换方法js设置cookie、读取cookie、删除js 将json字符串转换为json对象的js删除数组里的某个元素方法javascript深入理解js闭包javascript[js]获取url参数的代码js去除重复字符串两种实现方法打字效果动画的4种实现方法(超简单)javascript分析、压缩工具javascript analayui前端框架之table表数据的刷新方法radio 单选js动态添加的选项onchange事件基于javascript判断浏览器到底是关闭还是js类中定义原型方法的两种实现的区别react服务端渲染(总结)js 数据类型转换总结笔记
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved