目录
1、简单示例 add 函数
-
- 1、简单示例 add 函数
- 2、参数复用
- 3、提前确认
-
- 3.1、示例壹
- 3.2、示例贰
- 3.3、示例叁
- 4、延迟运行
- 5、通用的封装方法
-
- 5.1、初步封装
- 5.2、递归封装
- 6、经典面试题
// Currying 前 function add(x, y) { return x + y; }; // Currying 后 function curryingAdd(x) { return function (y) { return x + y; }; }; add(1, 2); // 3 curryingAdd(1)(2); // 3
实际上就是把 add 函数的 x, y 两个参数变成了先用一个函数接收 x 然后返回一个函数去处理 y 参数。就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
2、参数复用// Currying 前 function check(reg, txt) { return reg.test(txt); }; check(/\d+/g, 'test'); // false check(/[a-z]+/g, 'test'); // true // Currying 后 function curryingCheck(reg) { return function(txt) { return reg.test(txt); }; }; let hasNumber = curryingCheck(/\d+/g); let hasLetter = curryingCheck(/[a-z]+/g); hasNumber('test1'); // true hasNumber('testtest'); // false hasLetter('21212'); // false
示例是一个正则的校验,正常来说直接调用 check 函数就可以,但是如果有很多地方都要校验是否有数字,其实就是需要将第一个参数 reg 进行复用,这样别的地方就能够直接调用 hasNumber,hasLetter 等函数,让参数能够复用,调用起来也更方便。
3、提前确认 3.1、示例壹let on = function(element, event, handler) { if (document.addEventListener) { if (element && event && handler) { element.addEventListener(event, handler, false); }; } else { if (element && event && handler) { element.attachEvent('on' + event, handler); }; }; };3.2、示例贰
let on = (function() { if (document.addEventListener) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); }; }; } else { return function(element, event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); }; }; }; })(); // ()(); => ~function() {.. ..}();3.3、示例叁
// 上面就是把 isSupport 这个参数给先确定下来了 let on = function(isSupport, element, event, handler) { isSupport = isSupport || document.addEventListener; if (isSupport) { return element.addEventListener(event, handler, false); } else { return element.attachEvent('on' + event, handler); }; };
在做项目的过程中,封装一些 dom 操作可以说再常见不过,上面第一种写法也是比较常见,但是看看第二种写法,它相对于第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断。
4、延迟运行Function.prototype.bind = function (context) { let that = this; let args = Array.prototype.slice.call(arguments, 1); return function() { return that.apply(context, args); }; };
js 中经常使用的 bind,实现的机制就是 Currying。
5、通用的封装方法 5.1、初步封装let currying = function(fn) { // args 获取第一个方法内的全部参数 let args = Array.prototype.slice.call(arguments, 1); return function() { // 将后面方法里的全部参数和 args 进行合并 var newArgs = args.concat(Array.prototype.slice.call(arguments)); // 把合并后的参数通过 apply 作为 fn 的参数并执行 return fn.apply(this, newArgs); }; };
通过闭包把初步参数给保存下来,然后通过获取剩下的 arguments 进行拼接,最后执行需要 currying 的函数。但是,有缺陷,这样返回的只能多扩展一个参数,currying(a)(b)© 这样的话,就不支持多参数调用了。
5.2、递归封装function progressCurrying(fn, args) { let that = this; let len = fn.length; let args = args || []; return function() { let _args = Array.prototype.slice.call(arguments); Array.prototype.push.apply(args, _args); // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数 if (_args.length < len) { return progressCurrying.call(that, fn, _args); } // 参数收集完毕,则执行fn return fn.apply(this, _args); }; };
其实是在初步的基础上,加上了递归的调用,只要参数个数小于最初的fn.length,就会继续执行递归。
6、经典面试题function add() { // 第一次执行时,定义一个数组专门用来存储所有的参数 let _args = Array.prototype.slice.call(arguments); // 在内部声明一个函数, // 利用闭包的特性保存_args并收集所有的参数值 let _adder = function() { _args.push(...arguments); return _adder; }; // 利用toString隐式转换的特性, // 当最后执行时隐式转换,并计算最终的值返回 _adder.toString = function () { return _args.reduce(function (a, b) { return a + b; }); }; return _adder; }; add(1)(2)(3); // 6 add(1, 2, 3)(4); // 10 add(1)(2)(3)(4)(5); // 15 add(2, 6)(1); // 9