时候我们希望函数可以分步接受参数,并在所有参数都到位后得到执行结果。为了实现这种机制,我们先了解函数在Javascript中的应用过程:

1. 函数的“应用”(Function Application)

在 函数化程序语言中,函数(Function)不是被调用(invoked)或被执行(called)的,而是被应用的(applied)。在 Javascript中,函数是一个对象,因此它也可以被“应用”: Function.prototype.apply()。下面就是一个例子:
// define a function
var sayHi = function (who) {
return "Hello" + (who ? ", " + who : "") + "!";
};
// invoke a function
sayHi(); // "Hello"
sayHi('world'); // "Hello, world!"
// apply a function
sayHi.apply(null, ["hello"]); // "Hello, hello!"
从 这个例子可以看出,调用一个函数的结果和应用一个函数的结果相同。函数的apply()方法接收两个参数,第一个参数是个对象,在函数的执行时期绑定到函 数声明内部的this对象引用;另一个参数是一个数组,用来传递函数的参数。如果第一个参数是null,那么函数在执行时,this就指向global对 象:这就是函数(不包括函数内的方法)被执行(invoked)时发生的情况。
当函数内的方法被应用(applied)时,第一个参数就不能传null了,传递的应该是这个方法所属的那个对象:
var alien = {
sayHi: function (who) {
return "Hello" + (who ? ", " + who : "") + "!";
}
};
alien.sayHi('world'); // "Hello, world!"
sayHi.apply(alien, ["humans"]); // "Hello, humans!"
所以上面的例子里方法中的this引用方法所属的对象,而函数中的this,如果没有特别给定,引用的是global对象。
通过上面的两个例子可以看出,JavaScript里的函数执行其实就是函数的应用过程。除了apply,函数还有另一个类似的固有方法call,当函数只有一个参数时,call的第二个参数可以不用传一个数组,而是直接给出那个唯一的参数:
// the second is more efficient, saves an array
sayHi.apply(alien, ["humans"]); // "Hello, humans!"
sayHi.call(alien, "humans"); // "Hello, humans!"
 
2. 函数的部分应用 (Partial Application)
函数的执行过程既然是向它应用参数的过程,那我们是不是可以分步骤应用函数的参数呢?比如,我们有个add(x,y)的函数,它进行加法计算,是不是可以先应用一个参数,再应用下一个呢(注意,下面的代码并不合法,仅作为演示):
// for illustration purposes
// not valid JavaScript
// we have this function
function add(x, y) {
return x + y;
}
// and we know the arguments
add(5, 4);
// step 1 -- substitute one argument
function add(5, y) {
return 5 + y;
}
// step 2 -- substitute the other argument
function add(5, 4) {
return 5 + 4;
}
上面代码里的step1和step2不合法。但由于函数的执行过程就是它的应用过程,我们如果可以编程实现这一机制,比如这样:
var add = function (x, y) {
return x + y;
};
// full application
add.apply(null, [5, 4]); // 9
// partial application
var newadd = add.partialApply(null, [5]);
// applying an argument to the new function
newadd.apply(null, [4]); // 9

上面的代码中,partialApply返回一个新的函数,这个新的函数通过接受剩余的参数,完成加法。可惜的是Javascript里并不提供partialApply,所以我们必须自己实现它。

 
3. Curry化(Currying)
 Curry在这里并不是咖喱的意思,而是以Haskell Curry命名的一种编程模式,它把一个常规函数转化成可分步执行的函数:
// a curried add()
// accepts partial list of arguments
function add(x, y) {
var oldx = x, oldy = y;
if (typeof oldy === "undefined") { // partial
return function (newy) {
return oldx + newy;
};
}
// full application
return x + y;
}
// test
typeof add(5); // "function"
add(3)(4); // 7
// create and store a new function
var add2000 = add(2000);
add2000(10); // 2010
通过上面的代码,add就可以既接受一个参数,也可接受两个参数,当它接受两个参数时,就一次完成加法运算;当参数只有一个时,它返回一个利用第一个参数完成加法的新函数,以便在后续的执行过程中继续加法运算。上面的代码可以通过去掉oldx和oldy来进一步精减:
// a curried add
// accepts partial list of arguments
function add(x, y) {
if (typeof y === "undefined") { // partial
return function (y) {
return x + y;
};
}
// full application
return x + y;
}
这样可以分步执行的add函数就实现了。类似地,使用函数的apply方法,可以做出通用的分步执行函数:
function schonfinkelize(fn) {
var slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
return function () {
var new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
};
}
上面schonfinkelize 通过Array的slice方法,把arguments除了第1元素外(第1个元素是被curry的函数名)的其它所有元素都保存在 stored_args里,然后通过返回一个新的函数,并把之前保存的部分参数连接起来,完成函数分部执行的功能:
// a normal function
function add(x, y) {
return x + y;
}
// curry a function to get a new function
var newadd = schonfinkelize(add, 5);
newadd(4); // 9
// another option -- call the new function directly
schonfinkelize(add, 6)(7); // 13
也可多次执行schonfinkelize,让函数多次分步执行:
// a normal function
function add(a, b, c, d, e) {
return a + b + c + d + e;
}
// works with any number of arguments
schonfinkelize(add, 1, 2, 3)(5, 5); // 16
// two-step currying
var addOne = schonfinkelize(add, 1);
addOne(10, 10, 10, 10); // 41
var addSix = schonfinkelize(addOne, 2, 3);
addSix(5, 5); // 16

JavaScript基础Curry化(021)的更多相关文章

  1. React 应用设计之道 - curry 化妙用

    使用 React 开发应用,给予了前端工程师无限"组合拼装"快感.但在此基础上,组件如何划分,数据如何流转等应用设计都决定了代码层面的美感和强健性. 同时,在 React 世界里提 ...

  2. 前端之JavaScript基础

    前端之JavaScript基础 本节内容 JS概述 JS基础语法 JS循环控制 ECMA对象 BOM对象 DOM对象 1. JS概述 1.1. javascript历史 1992年Nombas开发出C ...

  3. javascript基础03

    javascript基础03 1. 算术运算符 后增量/后减量运算符 ++ ,-- 比较运算符 ( >, <, >=, <=, ==, !=,===,!== ) 逻辑运算符( ...

  4. javascript基础07

    javascript基础07 1.节点 元素.childNodes : 属性 只读 属性 子节点列表集合 元素.childNodes 只包含子节点,不包含孙节点 DOM节点的类型有很多种,w3c标准有 ...

  5. javascript基础系列(入门前须知)

    -----------------------小历史---------------------------- javascript与java是两种语言,他们的创作公司不同,JavaScript当时是借 ...

  6. 第三篇:web之前端之JavaScript基础

    前端之JavaScript基础   前端之JavaScript基础 本节内容 JS概述 JS基础语法 JS循环控制 ECMA对象 BOM对象 DOM对象 1. JS概述 1.1. javascript ...

  7. JavaScript基础:DOM操作详解

    本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. 前言 JavaScript的组成 JavaScript基础分为三个部分: ...

  8. Web前端-JavaScript基础教程上

    Web前端-JavaScript基础教程 将放入菜单栏中,便于阅读! JavaScript是web前端开发的编程语言,大多数网站都使用到了JavaScript,所以我们要进行学习,JavaScript ...

  9. javascript基础修炼(7)——Promise,异步,可靠性

    开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 别人是开发者,你也是 Promise技术是[javascript异步编程]这个话题中非常重要的,它一度让我感到熟悉 ...

随机推荐

  1. Java实现蓝桥杯 算法提高 线段和点

    算法提高 线段和点 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 有n个点和m个区间,点和区间的端点全部是整数,对于点a和区间[b,c],若a>=b且a<=c,称点a满 ...

  2. Java实现 LeetCode 699 掉落的方块(线段树?)

    699. 掉落的方块 在无限长的数轴(即 x 轴)上,我们根据给定的顺序放置对应的正方形方块. 第 i 个掉落的方块(positions[i] = (left, side_length))是正方形,其 ...

  3. Java实现 LeetCode 520 检测大写字母

    520. 检测大写字母 给定一个单词,你需要判断单词的大写使用是否正确. 我们定义,在以下情况时,单词的大写用法是正确的: 全部字母都是大写,比如"USA". 单词中所有字母都不是 ...

  4. Java实现 LeetCode 283 移动零

    283. 移动零 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必 ...

  5. Java实现 蓝桥杯 历届试题幸运数

    问题描述 幸运数是波兰数学家乌拉姆命名的.它采用与生成素数类似的"筛法"生成 . 首先从1开始写出自然数1,2,3,4,5,6,- 1 就是第一个幸运数. 我们从2这个数开始.把所 ...

  6. Linux笔记(第一天)

    一.命令 lscpu                               -- 查看cpu free                                 -- 内存查看 -m 以M ...

  7. PBFT共识算法

    拜占庭将军问题 我们已知的共识算法,Paxos.Raft解决的都是非拜占庭问题,也就是可以容忍节点故障,消息丢失.延时.乱序等,但节点不能有恶意节点.但如何在有恶意节点存在的情况下达成共识呢?BFT共 ...

  8. JavaScript转换json

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. [ARC060D] 最良表現

    题目   点这里看题目. 分析   由于 KMP 的失配数组有着天然的找循环节的功能,因此我们不难想到对原串进行两次 KMP ,一正一反.   可以发现如下的规律:   1. 原串无循环节,这个时候 ...

  10. matlab之指派问题(整数规划)

    1 c=[ ; ; ; ]; c=c(:);%将矩阵C按列拉直,然后赋给C,例如矩阵C=[,,;,,],操作完后就是列向量1,,,,, a=zeros(,); for i=: a(i,(i-)*+:* ...