高阶函数

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

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

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

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

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. nyoj 61——传纸条(一)——————【双线dp】

    传纸条(一) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5   描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行 ...

  2. 对C++ Local的经典分析(转)

    对C++ Local的经典分析 本贴转载自:再别流年的技术实验室 文章地址: http://kittsoft.xp3.biz/?p=86 “这个问题比你想象中复杂”(我也学下BS的风格,虽然这句话是我 ...

  3. 深入理解JavaScript系列(18):面向对象编程之ECMAScript实现(推荐)

    介绍 本章是关于ECMAScript面向对象实现的第2篇,第1篇我们讨论的是概论和CEMAScript的比较,如果你还没有读第1篇,在进行本章之前,我强烈建议你先读一下第1篇,因为本篇实在太长了(35 ...

  4. artDialog组件应用学习(二)

    一.没有操作选项的对话框 预览: html前台引入代码:(之后各种效果对话框引入代码致,调用方法也一样,就不一一写入) <script type="text/javascript&qu ...

  5. JavaScript typeof运算符和数据类型

    // js有6种数据类型:Undefined.Null.Boolean.String.Number.Object //(01)typeof console.log(typeof undefined); ...

  6. springboot vue组件写的个人博客系统

    个人写的博客管理系统,学习java不到一年 欢迎探讨交流学习 https://github.com/Arsense/ssmBlog  项目地址 如果觉得好的 帮忙star一下 谢谢! 基本技术 环境: ...

  7. js系列之js简介

    该系列教程都来源于:廖雪峰老师的博客 JavaScript是世界上最流行的脚本语言,因为你在电脑.手机.平板上浏览的所有的网页,以及无数基于HTML5的手机App,交互逻辑都是由JavaScript驱 ...

  8. vue——组件

    一.组件概念 vue的核心基础就是组件的使用,玩好了组件才能将前面学的基础更好的运用起来.组件的使用更使我们的项目解耦合.更加符合vue的设计思想MVVM. // 定义一个名为 button-coun ...

  9. 2018.10.17NOIP模拟赛解题报告

    心路历程 预计得分:\(100 + 100 +100\) 实际得分:\(100 + 100 + 60\) 辣鸡模拟赛.. 5min切掉T1,看了一下T2 T3,感觉T3会被艹爆因为太原了.. 淦了20 ...

  10. css 小常识

    一.vertical-align可以采用负值(正/负值根据基线上下移动),也可以采用百分比值,而这个百分比值不是相对于字体大小或者其他什么属性计算的,而是相对于line-height计算的. 此外,w ...