在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1 什么是函数柯里化在计算机科学中,柯里化( 什么意思?简单来说,柯里化是一项技术,它用来改造多参数的函数。 比如: // 这是一个接受3个参数的函数 const add = function(x, y, z) { return x + y + z } 我们将它变换一下,可以得到这样一个函数: // 接收一个单一参数 const curryingAdd = function(x) { // 并且返回接受余下的参数的函数 return function(y, z) { return x + y + z } } 这样有什么区别呢?从调用上来对比: // 调用add add(1, 2, 3) // 调用curryingAdd curryingAdd(1)(2, 3) // 看得更清楚一点,等价于下面 const fn = curryingAdd(1) fn(2, 3) 可以看到,变换后的的函数可以分批次接受参数,先记住这一点,下面会讲用处。甚至fn( 如下: const curryingAdd = function(x) { return function(y) { return function(z) { return x + y + z } } } // 调用 curryingAdd(1)(2)(3) // 即 const fn = curryingAdd(1) const fn1 = fn(2) fn1(3) 上面的两次变换过程,就是函数柯里化。 简单讲就是把一个多参数的函数 那么费这么大劲将函数柯里化有什么用呢? 2 柯里化的作用和特点2.1 参数复用工作中会遇到的需求:通过正则校验电话号、邮箱、身份证是否合法等等 于是我们会封装一个校验函数如下: /** * @description 通过正则校验字符串 * @param {RegExp} regExp 正则对象 * @param {String} str 待校验字符串 * @return {Boolean} 是否通过校验 */ function checkByRegExp(regExp, str) { return regExp.test(str) } 假如我们要校验很多手机号、邮箱,我们就会这样调用: // 校验手机号 checkByRegExp(/^1\d{10}$/, '15152525634'); checkByRegExp(/^1\d{10}$/, '13456574566'); checkByRegExp(/^1\d{10}$/, '18123787385'); // 校验邮箱 checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, '[email protected]'); checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, '[email protected]'); checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, '[email protected]'); 貌似没什么问题,事实上还有改进的空间
我们试着使用函数柯里化来改进: // 将函数柯里化 function checkByRegExp(regExp) { return function(str) { return regExp.test(str) } } 于是我们传入不同的正则对象,就可以得到功能不同的函数: // 校验手机 const checkPhone = curryingCheckByRegExp(/^1\d{10}$/) // 校验邮箱 const checkEmail = curryingCheckByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/) 现在校验手机、邮箱的代码就简单了,并且可读性也增强了 // 校验手机号 checkPhone('15152525634'); checkPhone('13456574566'); checkPhone('18123787385'); // 校验邮箱 checkEmail('[email protected]'); checkEmail('[email protected]'); checkEmail('[email protected]'); 这就是参数复用:我们只需将第一个参数 通用函数(如 有的时候同一种规则可能会反复使用(比如校验手机的参数),这就造成了代码的重复,利用柯里化就能够消除重复,达到复用参数的目的。 柯里化的一种重要思想:降低适用范围,提高适用性 2.2 提前返回在 这时我们会写一个兼容各浏览器版本的代码: /** * @description: * @param {object} element DOM元素对象 * @param {string} type 事件类型 * @param {Function} fn 事件处理函数 * @param {boolean} isCapture 是否捕获 * @return {void} */ function addEvent(element, type, fn, isCapture) { if (window.addEventListener) { element.addEventListener(type, fn, isCapture) } else if (window.attachEvent) { element.attachEvent("on" + type, fn) } } 我们用 柯里化处理: function curryingAddEvent() { if (window.addEventListener) { return function(element, type, fn, isCapture) { element.addEventListener(type, fn, isCapture) } } else if (window.attachEvent) { return function(element, type, fn) { element.attachEvent("on" + type, fn) } } } const addEvent = curryingAddEvent() // 也可以用立即执行函数将上述代码合并 const addEvent = (function curryingAddEvent() { ... })() 现在我们得到的 这就是提前返回或者说提前确认,函数柯里化后可以提前处理部分任务,返回一个函数处理其他任务 另外,我们可以看到, 逻辑上其实是可以改成: let mode = window.addEventListener ? 0 : 1; function addEvent(mode, element, type, fn, isCapture) { if (mode === 0) { element.addEventListener(type, fn, isCapture); } else if (mode === 1) { element.attachEvent("on" + type, fn); } } // 这样柯里化后就可以先接受一个参数了 function curryingAddEvent(mode) { if (mode === 0) { return function(element, type, fn, isCapture) { element.addEventListener(type, fn, isCapture) } } else if (mode === 1) { return function(element, type, fn) { element.attachEvent("on" + type, fn) } } } 当然没必要这么改~ 2.3 延迟执行事实上,上述正则校验和事件监听的例子中已经体现了延迟执行。
返回的函数都不会立即执行,而是等待调用。 3 封装通用柯里化工具函数#
|
请发表评论