一個函數 若是輸入參數包含函數 或 返回值是函數,就稱爲高階函數。javascript
這篇文章介紹高階函數的一個子集:輸入
fn
,輸出fn'
。
按fn
與fn'
功能是否一致【即相同輸入是否始終對應相同輸出】,把這類高階函數的做用分爲兩類:java
- 包裝函數:功能一致
- 修改函數:功能不一致
從斐波那契數列開始。react
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)); console.time("fib"); fib(42); console.timeEnd("fib");
函數式青年session
const timed = fn => (...args) => { console.time(fn.name); const result = fn(...args); console.timeEnd(fn.name); return result; }; const fib = n => (n <= 1 ? 1 : fib(n - 1) + fib(n - 2)); timed(fib)(42);
普通青年async
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) => { console.time(fn.name); const result = fn(...args); console.timeEnd(fn.name); 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) => { console.time(fn.name); const result = fn(...args); console.timeEnd(fn.name); return result; }; timed(fib)(42);
場景:
發送請求,若是後臺返回 session 超時,彈出從新登陸提示框。
發出多個請求,後臺都返回 session 超時錯誤,只但願彈一個從新登陸提示框。post
const once = fn => { let executed = false; return (...args) => { if (!executed) { executed = true; fn(...args); } }; }; const showLogoutWin = once(() => { // ... });
場景:
輸入框 change 事件觸發向後臺查詢
爲消除沒必要要的查詢
用戶連續輸入時不觸發查詢,當 200ms 內沒有新的輸入時,才向後臺查詢性能
const debounce = (fn, ms = 200) => { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn(...args), ms); }; };
場景:
根據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"); } });
場景:
由於網絡不穩定,請求可能出錯,出錯後從新請求,直到獲得響應爲止。
let res; while (true) { try { res = await get(path); break; } catch (err) { console.log(err); } }
把出錯重試的邏輯提取到高階函數中
const tryUntilSucceeded = fn => async (...args) => { while (true) { try { return await fn(...args); } catch (err) { console.log(err); } } }; const enhancedGet = tryUntilSucceeded(get); const enhancedPost = tryUntilSucceeded(post); const resGet = await enhancedGet(path); const resPost = await enhancedPost(path);
兩個代碼塊同樣,把這個代碼塊提取出來,封成一個函數,減小代碼重複,這個技巧你們都知道;
兩段代碼流程同樣,用高階函數把公共的流程提取出來,減小代碼重複,這個技巧知道的人就很少了。能夠類比react的高階組件,道理是同樣的。