高阶函数 - Higher Order Function
一个函数 如果输入参数包含函数 或 返回值包含函数,就称为高阶函数。这篇文章介绍高阶函数的一个子集:输入 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都可以提取到公共库,供别的地方使用
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!