JavaScript函数柯里化

一、定义:

柯里化(Currying)是把承受多个参数的函数变换成承受一个繁多参数(最后函数的第一个参数)的函数,并且返回承受余下的参数且返回后果的新函数的技术。

通过一个简略的例子解释一下:

function add(a, b) {    return a + b}add(1, 2); // 3

将函数add转化为柯里化函数_add

function _add(a){    return function(b){        return a + b    }}_add(1)(2);  // 3

函数add和函数_add是等价的。

_add可能解决add的所有残余参数,因而柯里化也被称为局部求值

实际上就是把add函数的a,b两个参数变成了先用一个函数接管a而后返回一个函数去解决b参数。

当初思路应该就比拟清晰了:只传递给函数一部分参数来调用,让它返回一个函数去解决剩下的参数。

二、柯里化函数的作用

1、参数复用

案例:拼接地址

依照一般思路去拼接一个地址

// 拼接地址function getUrl(protocol, hostname, pathname) {    return `${protocol}${hostname}${pathname}`;}const url1 = getUrl('https://', 'www.baidu.com', '/hasa');const url2 = getUrl('https://', 'www.baidu.com', '/saandsa');const url3 = getUrl('https://', 'www.baidu.com', '/hasak');console.log(url1, url2, url3)

每次调用getUrl参数的时候都要反复的传入参数'https://', 'www.baidu.com'。

柯里化封装之后:

function curry(protocol) {    return function (hostname, pathname) {        return `${protocol}${hostname}${pathname}`;    }}const url_curry = curry('https://');const url1 = url_curry('www.baidu.com', '/hasa');const url2 = url_curry('www.baidu.com', '/saandsa');const url3 = url_curry('www.baidu.com', '/hasak');console.log(url1, url2, url3)

很显著,通过柯里化封装之后,之后再进行地址拼接的时候,缩小了参数个数,升高了代码反复率。

2、提前确认

案例:兼容IE浏览器事件的监听办法(IE is dead

传统的办法:

/** @param    element       Object      DOM元素对象* @param    type          String      事件类型* @param    listener      Function    事件处理函数* @param    useCapture    Boolean     是否捕捉*/var addEvent = function (element, type, listener, useCapture) {    if (window.addEventListener) {        element.addEventListener(type, function (e) {            listener.call(element, e)        }, useCapture)    } else {        element.attachEvent('on' + type, function (e) {            listener.call(element, e)        })    }}

缺点就是,每次对DOM元素进行事件绑定时都须要从新进行判断,其实对于事件监听网页一公布浏览器曾经确定了,就能够通晓浏览器到底是须要哪一种监听形式。所以咱们能够让判断只执行一次。

柯里化封装之后:

var curEvent = (function () {    if (window.addEventListener) {        return function (element, type, listener, useCapture) { // return funtion            element.addEventListener(type, function () {                listener.call(element)            }, useCapture)        }    } else {        return function (element, type, listener) {            element.attachEvent('on' + type, function () {                listener.call(element)            })        }    }})var addEvent = curEvent();addEvent(element, "click", listener)

立刻执行函数,在触发屡次事件也仍旧只会触发一次if条件判断。

3、提早执行

案例:钓鱼统计分量

传统的办法:

let fishWeight = 0;const addWeight = function(weight) {    fishWeight += weight;};addWeight(2.3);addWeight(6.5);addWeight(1.2);addWeight(2.5);console.log(fishWeight);  

每次执行addWeight办法时,都做了一次鱼的体重的加和。

柯里化封装后:

function curryWeight(fn) {    let _fishWeight = [];    return function () {        if (arguments.length === 0) {            return fn.apply(null, _fishWeight);        } else {            _fishWeight = _fishWeight.concat([...arguments]);        }    }}function addWeight() {    let fishWeight = 0;    for (let i = 0, len = arguments.length; i < len; i++) {        fishWeight += arguments[i];    }    return fishWeight;}const _addWeight = curryWeight(addWeight);_addWeight(6.5);_addWeight(1.2);_addWeight(2.3);_addWeight(2.5);console.log(_addWeight())

在执行_addWeight办法时,并没有做鱼的体重的加和,之后在最初一次执行_addWeight()时,才做了加和。做到了提早执行addWeight办法的成果。

三、柯里化函数的实现

function curry() {    let args = [...arguments];    let inner = function () {        args.push(...arguments);        return inner;    }        // 核心内容:隐式转换,调用了外部的toString    inner.toString = function () {        return args.reduce(function (pre, next) {            return pre + next;        })    }    return inner;}const result = curry(1)(2);console.log(Number(result));

四、柯里化总结

函数的柯里化,须要依赖参数以及递归,通过拆分参数的形式,来调用一个多参数的函数办法,以达到缩小代码冗余,减少可读性的目标。

性能方面:

  • 存取arguments对象通常要比存取命名参数要慢一点;
  • 一些老版本的浏览器在arguments.length的实现上是相当慢的;
  • 应用fn.apply( … ) 和 fn.call( … )通常比间接调用fn( … ) 略微慢点;
  • 创立大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上。

利用场景:

  1. 缩小反复传递不变的局部参数;
  2. 将柯里化后的callback参数传递给其余函数。