一文带你了解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( … ) 稍微慢点;
- 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上。
应用场景:
- 减少重复传递不变的部分参数;
- 将柯里化后的callback参数传递给其他函数。
到此这篇关于一文带你了解JavaScript函数柯里化的文章就介绍到这了,更多相关JavaScript函数柯里化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!