高阶函数 - Higher Order Function
程序员文章站
2022-06-27 22:05:46
一个函数 如果输入参数包含函数 或 返回值是函数,就称为高阶函数。 这篇文章介绍高阶函数的一个子集:输入 fn,输出 fn'。 ......
一个函数 如果输入参数包含函数 或 返回值是函数,就称为高阶函数。
这篇文章介绍高阶函数的一个子集:输入 fn
,输出 fn'
。
按fn
与fn'
功能是否一致【即相同输入是否始终对应相同输出】,把这类高阶函数的作用分为两类:
- 包装函数:功能一致
- 修改函数:功能不一致
包装函数
从斐波那契数列开始。
const fib = n => (n <= 1 ? 1 : fib(n - 1) + fib(n - 2)); fib(42);
记录执行时间
普通青年
const fib = n => (n <= 1 ? 1 : fib(n - 1) + fib(n - 2)); const start = new date().gettime(); fib(42); console.log(new date().gettime() - start + "ms");
函数式青年
const timed = fn => (...args) => { const start = new date().gettime(); const result = fn(...args); console.log(new date().gettime() - start + "ms"); return result; }; const fib = n => (n <= 1 ? 1 : fib(n - 1) + fib(n - 2)); timed(fib)(42);
性能优化 memorize
普通青年
const memory = {}; const fib = n => { if (n <= 1) return 1; else { if (memory[n]) return memory[n]; else { memory[n] = fib(n - 1) + fib(n - 2); return memory[n]; } } }; const timed = fn => (...args) => { const start = new date().gettime(); const result = fn(...args); console.log(new date().gettime() - start + "ms"); return result; }; timed(fib)(42);
函数式青年
const memorize = fn => { const memory = {}; return arg => { if (memory[arg]) return memory[arg]; else { memory[arg] = fn(arg); return memory[arg]; } }; }; const fib = memorize(n => (n <= 1 ? 1 : fib(n - 1) + fib(n - 2))); const timed = fn => (...args) => { const start = new date().gettime(); const result = fn(...args); console.log(new date().gettime() - start + "ms"); return result; }; timed(fib)(42);
修改函数
once
场景:
发送请求,如果后台返回 session 超时,弹出重新登录提示框。
发出多个请求,后台都返回 session 超时错误,只希望弹一个重新登录提示框。
const once = fn => { let executed = false; return (...args) => { if (!executed) { executed = true; fn(...args); } }; }; const showlogoutwin = once(function() { // ... });
debounce
场景:
输入框 change 事件触发向后台查询
为消除不必要的查询
用户连续输入时不触发查询,当 200ms 内没有新的输入时,才向后台查询
const debounce = (fn, ms = 200) => { let timeoutid; return (...args) => { // you may have a try }; };
更多实际场景
validaterequired
场景:
根据rule.required
判断空值时是否报错,这段逻辑出现在多个 validator 中。
const ipv4validator = (rule, value, callback) => { if (value) { if (ipv4regexp.test(value)) { callback(); } else { callback("请输入合法ip"); } } else { if (rule.required) { callback("该域为必填项"); } else { callback(); } } };
const validaterequired = (validator, msg = "该域为必填项") => ( rule, value, callback ) => { if (value) { validator(rule, value, callback); } else { if (rule.required) { callback(msg); } else { callback(); } } }; const ipv4validator = validaterequired((rule, value, callback) => { if (ipv4regexp.test(value)) { callback(); } else { callback("请输入合法ip"); } });
tryuntilsucceeded
场景:
因为网络不稳定,请求可能出错,出错后重新请求,直到得到响应为止。
let res; while (true) { try { res = await get(path); break; } catch (err) { console.log(err); } }
每个请求都套一层while
,写起来太费事、太重复。
const tryuntilsucceeded = fn => async (...args) => { // you may have a try }; const enhancedget = tryuntilsucceeded(get); const enhancedpost = tryuntilsucceeded(post); const resget = await enhancedget(path); const respost = await enhancedpost(path);
小结
恰当使用高阶函数有以下好处:
- 函数做的事情更单一
像上面斐波那契数列的例子,fib
只关心数列的计算逻辑
记录时间、性能优化的事情交给timed
和memorized
处理
这样fib
逻辑简单,不容易出错 - 代码可复用,减少了代码重复
像上面的timed
,memorized
......tryuntilsucceeded
都可以提取到公共库,供别的地方使用
推荐阅读