乐趣区

关于前端:JavaScript函数柯里化

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 参数传递给其余函数。
退出移动版