高阶函数

满足下列条件之一的函数:

函数作为参数被传递(如回调函数);

函数可以作为返回值输出;

一些内置高阶函数的例子:

Array.prototype.map

map()方法通过调用对输入数组中的每个元素调用回调函数来创建一个新数组。

map()方法将获取回调函数的每个返回值,并使用这些值创建一个新数组。

map()方法的回调函数总共接收3个参数:element,index和array。

例子:

假设我们有一个数字数组,我们想创建一个新数组,新数组的每个值是原数组对应值的两倍。

不使用高阶函数:

const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i ++) {
arr2.push(arr[i] * 2);
} console.log(arr2); //[2, 4, 6]

使用高阶函数:

const arr1 = [1, 2, 3];
const arr2 = arr1.map(item=> item * 2);
console.log(arr2);

Array.prototype.filter

filter()方法会创建一个新数组,其中包含所有通过回调函数测试的元素。传递给filter()方法的回调函数接受3个参数:element,index和array

例子:

假设我们有一个包含数字值的数组,选择其中的数值创建一个数值大于18的数组

不使用高阶函数:

const arr1 = [1, 2, 19, 20];
const arr2 = [];
for(let i = 0; i < arr1.length; i ++) {
if(arr1[i] > 18) {
arr2.push(arr1[1])
}
} console.log(arr2)

使用高阶函数:

const arr1 = [1, 2, 19, 20];
const arr2 = arr1.filter(item=> item > 18); console.log(arr2)

Array.prototype.reduce

reduce()方法对调用数组的每个元素执行回调函数,最后生成一个单一的值并返回。

reduce()方法接受两个参数:redecer函数(回调),一个可选的initialValue

reducer函数(回调)接受4个参数:accumulater, currentValue, currentIndex, sourceArray

如果提供了 initialValue,则累加器将等于 initialValue,currentValue 将等于数组中的第一个元素。

如果没有提供 initialValue,则累加器将等于数组中的第一个元素,currentValue 将等于数组中的第二个元素。

例子:

对一个数字数组的求和:

不使用高阶函数:

const arr = [1, 2, 7];
let sum = 0;
for(let i = 0; i< arr.length; i++) {
sum = sum + arr[i]
}
// 10

使用高阶函数:

const arr = [1, 2, 7];
const sum = arr.reduce((accumulator,currentValue)=>
return accumulator + currentValue )
//

还可以为它提供初始值:

const arr = [1, 2, 7];
const sum = arr.reduce((accumulator,currentValue)=>
return accumulator + currentValue,10 )
//

柯里化

柯里化(currying)又称部分求值。一个currying的函数首先会接受一些参数,接受这些参数之后,函数并不会立即求值,而是继续返回另一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

计算每天的花销

var currying = function(fn) {

    var args = [];

    return function() {
if (arguments.length === 0) {
return fn.apply(this, args);
} else {
Array.prototype.push.apply(args, arguments);
return arguments.callee;
}
}
} cost = function(){
var sum = 0;
for (var i = 0, len = arguments.length; i < len; i++) {
sum += arguments[i];
} return sum;
}
var cost = currying(cost); cost(100);
cost(200);
alert(cost())

通俗地讲,柯里化就是一个部分配置多参数函数的过程,每一步都返回一个接受单个参数的部分配置好的函数。一些极端的情况可能需要分很多次来部分配置一个函数,比如说多次相加:

multiPlus(1)(2)(3); // => 6

上代码

// ES5
function curry(fn) {
function _c(restNum, argsList) {
return restNum === 0 ?
fn.apply(null, argsList) :
function(x) {
return _c(restNum - 1, argsList.concat(x));
};
}
return _c(fn.length, []);
} // ES6
const curry = fn => {
const _c = (restNum, argsList) => restNum === 0 ?
fn(...argsList) : x => _c(restNum - 1, [...argsList, x]); return _c(fn.length, []);
} /***************** 使用 *********************/ var plus = curry(function(a, b) {
return a + b;
});

分析:

我们需要一个curry函数,它接受一个待柯里化的函数为参会素,返回一个用于接收一个参数的函数,接收到的参数放到一个列表中,当参数数量足够时,执行原函数并返回结果。

实现方式:

简单思考可以知道,柯里化部分配置函数的步骤数等于 fn 的参数个数,也就是说有两个参数的 plus 函数需要分两步来部分配置。函数的参数个数可以通过fn.length获取。

总的想法就是每传一次参,就把该参数放入一个参数列表 argsList 中,如果已经没有要传的参数了,那么就调用fn.apply(null, argsList)将原函数执行。要实现这点,我们就需要一个内部的判断函数 _c(restNum, argsList),函数接受两个参数,一个是剩余参数个数 restNum,另一个是已获取的参数的列表 argsList_c 的功能就是判断是否还有未传入的参数,当 restNum 为零时,就是时候通过fn.apply(null, argsList)执行原函数并返回结果了。如果还有参数需要传递的话,也就是说 restNum 不为零时,就需要返回一个单参数函数

function(x) {
return _c(restNum - 1, argsList.concat(x));
}

来继续接收参数。这里形成了一个尾递归,函数接受了一个参数后,剩余需要参数数量 restNum 减一,并将新参数 x 加入 argsList 后传入 _c 进行递归调用。结果就是,当参数数量不足时,返回负责接收新参数的单参数函数,当参数够了时,就调用原函数并返回。

现在再来看:

function curry(fn) {
function _c(restNum, argsList) {
return restNum === 0 ?
fn.apply(null, argsList) :
function(x) {
return _c(restNum - 1, argsList.concat(x));
};
}
return _c(fn.length, []); // 递归开始
}

可以使用ES6写法,用数组结构和箭头函数:

// ES6
const curry = fn => {
const _c = (restNum, argsList) => restNum === 0 ?
fn(...argsList) : x => _c(restNum - 1, [...argsList, x]); return _c(fn.length, []);
}

另外一种方法:

function curry(fn) {
const len = fn.length;
return function judge(...args1) {
return args1.length >= len ?
fn(...args1):
function(...args2) {
return judge(...[...args1, ...args2]);
}
}
} // 使用箭头函数
const curry = fn => {
const len = fn.length;
const judge = (...args1) => args1.length >= len ?
fn(...args1) : (...args2) => judge(...[...args1, ...args2]);
return judge;
}

JS高阶函数与函数柯里化的更多相关文章

  1. Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化

    承接上文:Java函数式编程:一.函数式接口,lambda表达式和方法引用 这次来聊聊函数式编程中其他的几个比较重要的概念和技术,从而使得我们能更深刻的掌握Java中的函数式编程. 本篇博客主要聊聊以 ...

  2. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  3. JS的防抖,节流,柯里化和反柯里化

    今天我们来搞一搞节流,防抖,柯里化和反柯里化吧,是不是一看这词就觉得哎哟wc,有点高大上啊.事实上,我们可以在不经意间用过他们但是你却不知道他们叫什么,没关系,相信看了今天的文章你会有一些收获的 节流 ...

  4. JS高阶---变量与函数提升

    大纲: 主体: 案例1: 接下来在控制台source里进行断点测试 打好断点后,在控制台测试window .

  5. JS的闭包、高阶函数、柯里化

    本文原链接:https://cloud.tencent.com/developer/article/1326958 https://cloud.tencent.com/developer/articl ...

  6. js高阶函数应用—函数柯里化和反柯里化(二)

    第上一篇文章中我们介绍了函数柯里化,顺带提到了偏函数,接下来我们继续话题,进入今天的主题-函数的反柯里化. 在上一篇文章中柯里化函数你可能需要去敲许多代码,理解很多代码逻辑,不过这一节我们讨论的反科里 ...

  7. Python高阶函数及函数柯里化

    1 Python高阶函数 接收函数为参数,或者把函数作为结果返回的函数为高阶函数. 1.1 自定义sort函数 要求:仿照内建函数sorted,自行实现一个sort函数.内建函数sorted函数是返回 ...

  8. 从 ES6 高阶箭头函数理解函数柯里化

    前言:第一次看到多个连续箭头函数是在一个 react 项目中,然鹅确认了下眼神,并不是对的人,因为看得一脸懵逼.em......于是开始各种搜索,先是知道了多个连续箭头函数就是 es6 的多次柯里化的 ...

  9. JS 函数的柯里化与反柯里化

    ===================================== 函数的柯里化与反柯里化 ===================================== [这是一篇比较久之前的总 ...

  10. 浅析 JavaScript 中的 函数 currying 柯里化

    原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...

随机推荐

  1. JavaWeb技术

    1.简介 Java Web是用Java技术来解决相关web互联网领域的技术总和.web包括:web服务器和web客户端两部分.Java在客户端的应用有java applet,不过使用得很少,Java在 ...

  2. python的传递实参

    你经常会发现,向函数传递列表很有用,这种列表包含的可能是名字.数字或更复杂的对象(如字典).将列表传递给函数后,函数就能直接访问其内容 1.在函数中修改列表 将列表传递给函数后,函数就可对其进行修改. ...

  3. cf1072B. Curiosity Has No Limits(枚举)

    题意 题目链接 给出两个序列\(a, b\),求出一个序列\(t\),满足 \[a_i = t_i | t_{i + 1}\] \[b_i = t_i \& t_{i + 1}\] 同时,\( ...

  4. Web前端面试指导(十二):::before 和:before有什么区别?

    题目点评 这个问题看来很简单,但如果之前没有琢磨这个问题,给人感觉也是门头一垂,听到这个题目就懵逼了,因为原来从来没有注意过这个问题,即便有注意这个问题也不能很好的回答清楚.回答的技巧就是从相同点和不 ...

  5. css一个很好用的hover显示

    之前一直想在样式实现,hover时显示其他div,今天终于找到了,(*^▽^*) .a悬停时显示.b .a:hover .b { display: block; }

  6. HTML字符实体名称/实体编号

    字符实体对英文的大小写敏感! 字符实体一: 显示结果 描述 实体名称 实体编号   空格     < 小于号 < < > 大于号 > > & 和号 & ...

  7. Python进阶篇四:Python文件和流

    摘要: Python对于文件和流的操作与其他编程语言基本差不多,甚至语句上比其他语言更为简洁.文件和流函数针对的对象除了这两者之外还有,类文件(file-like),即python中只支持读却不支持写 ...

  8. javascript对象(2)

    这个对象,不是那个对象.续更第二篇.. 昨天说了对象的基本概念以及创建,今天来说一下它的其他方法: 1.访问属性的两种方式:点语法.[]语法 var dog =new Object(); dog.na ...

  9. .NET开源工作流RoadFlow-表单设计-组织机构选择

    组织机构选择即在表单中添加组织机构选择框. 选择范围: 1.发起者部门:只能在发起者同一个部门中选择. 2.处理者部门:只能在当前处理者同一个部门中选择. 3.自定义:自己指定一个选择范围. 选择类型 ...

  10. VC++上机实习

    I.课程设计基本练习题目(18分×4) [A组]请从以下1-3题中任意选做一题 1.输出1至100之间每位数的乘积大于每位数的和的数,例如对于数字12,有1*2<1+2,故不输出该数:对于27, ...