一、函數式編程定義
"函數式編程"是一種編程范式,也就是如何編寫程序的方法論。主要思想是把運算過程盡量寫成一系列嵌套的函數調用。即函數式編程要求使用函數,把運算過程定義為不同的函數。
二、函數式編程特性
- 無副作用
- 不可變數據
- 純函數
- 函數組合
- 函數柯里化
三、看個栗子
計算若干個數之和:
function add(x, y) {
return x + y;
}
function Reduce(...args) {
// 返回值
return args.reduce(add);
}
console.log(Reduce(1,2,3));
計算若干數之間的乘積:
function mul(x, y) {
return x * y;
}
function Reduce(...args) {
// 返回值
return args.reduce(mul);
}
console.log(Reduce(1,2,3));
若干個數組的拼接:
function concat(x, y) {
return x.concat(y);
}
function Reduce(...args) {
// 返回值
return args.reduce(concat);
}
console.log(Reduce([1,2],[5,6]));
可以看到,如果我們還有更多類似函數,我們還得對 Reduce 函數進行改寫。
至此,我們完全可以一次性抽取出一個通用的版本 Reduce :
function Reduce(fn) {
return function (...args){
return args.reduce(fn.bind(this));
}
}
let add = Reduce((x, y) => x + y);
// wrong: let add = iterative((x, y) =>{ x + y});
console.log(add(1,2,3));
通過向 Reduce 方法中傳遞一個特定功能的函數,來返回一個具備增強能力的新函數。這樣我們可以像操作數據那樣操作一組函數,使得這些函數具備某些新的能力。
這就是過程抽象的基本思想。這些變換函數的函數我們稱之為“高階函數”(它們自身輸入函數或返回函數)。
四、過程抽象的具體應用
- 控制函數調用的頻率
- 只調用一次的情況 (exp: 觸發(fā)點擊事件的回調函數限制其只調用一次)
function once(fn) {
return function (...args) {
if(fn) {
let res = fn.apply(this, args);
fn = null;
return res;
}
}
}
function foo(idx) {
console.log(idx);
}
foo(1);
foo(2);
foo(3);
let foo = once(foo);
foo(11);
foo(22);
foo(33);
場景代碼:https://code.h5jun.com/buzi/2/edit?html,js,output
- 每隔一定的時間觸發(fā)函數的執(zhí)行;
節(jié)流:常用來防止按鈕被重復點擊,防止 resize、scroll 和 mousemove 事件過于頻繁地觸發(fā)等。
這種方式也可避免定義全局的定時器變量,不會污染該變量。
function throttle(fn, wait=500) {
let timer;
return function (...args) {
if(timer == null) { // undefined == null // true undefined === null //false 這里不要寫成 嚴格相等 不然永遠執(zhí)行不進去
timer = setTimeout(() => timer = null, wait)
return fn.apply(this, args);
}
}
}
//限制button在500ms內只能被點擊一次:
//快速連續(xù)不停的點擊,按鈕只會有規(guī)律的每500ms點擊有效
button.addEventListener('click', throttle(() => {
console.log('hhh')
}))
// function once(fn) {
// fn = null; 不會改變外部的實參
//}
//function hh(){alert(11)}
//once(hh)
//console.log(hh)// function hh(){alert(11)}
// 類似于
function a(o){
o = 1; // o.aa = 22;
}
var obj = {
aa: 11
}
a(obj);
console.log(obj);
防抖動:希望函數在某些操作執(zhí)行完成之后被觸發(fā)。
輸入框搜索數據時,為了限制從服務器讀取數據的頻率,我們可以等待用戶輸入結束 100ms 之后再觸發(fā)查詢;或者點擊提交按鈕獲取數據,防止頻繁的請求數據,限制用戶提交完一段時間后再請求數據。(想想這里防抖動與上面節(jié)流的區(qū)別)
function debounce(fn, wait=500) {
let timer;
return function (...args) {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
timer = null;
return fn.apply(this, args)
}, wait)
}
}
// 快速連續(xù)不停的點擊,永遠不會觸發(fā),直到停止點擊,函數被執(zhí)行一次
button.addEventListener('click', debounce(() => {
console.log('hhh');
getMoreData();
}))
節(jié)流和防抖動都是控制函數在一定時間內的執(zhí)行次數,例如規(guī)定的時間間隔是500ms,如果連續(xù)在小于這個時間間隔內調用防抖動函數,該函數永遠不會被執(zhí)行(直到停止點擊,觸函數觸發(fā)一次);而節(jié)流函數會在每到達一段間隔的時間內有規(guī)律的被執(zhí)行。
詳見一句話說清楚.throttle和.debounce的區(qū)別
References
函數式編程離我們有多遠?
一句話說清楚.throttle和.debounce的區(qū)別
實例解析防抖和節(jié)流函數