[前端漫談_3] 從 filter 聊到 Promise
在學習前端的時候,我總是能聽到很多高階詞彙,比如今天會聊到的函數語言程式設計(Functional Programming) &高階函式 (Higher-order function) 。 但是當你真正的理解什麼是函數語言程式設計 &高階函式 的時候,也許會發現,你幾乎每天都會用到它,只是你不知道那就是高階函式 / 函數語言程式設計。
JavaScript 中的函式
在javascript
中,函式是一種值,舉個例子:
const double = function (x) { return x * 2 } 複製程式碼
我們把一個函式作為值,賦給了變數double
,這在我們的程式碼中很常見對嗎?
你是不是經常會聽到或者看到這樣一句話:“在 JavaScript 中函式是一等公民”
粗看很不好理解,但是它的意思很簡單:函式和 字串/number
沒有什麼不一樣,它可以宣告為變數,也可以作為引數傳入到其他函式中。
什麼是高階函式?
什麼是高階函式?其實上一段我們已經說過了,我們可以把函式A作為引數傳入到另一個函式B中,那麼接收函式作為引數的函式B,就是高階函式 ,這只是方便大家理解,高階函式的定義是:
"一個函式的引數是另一個函式,或者一個函式的返回值是另一個函式"
高階函式的例子
filter
說到filter()
你肯定不陌生,他接收一個回撥函式作為它的引數,所以它是一個典型的高階函式,舉個例子:
我們有這麼一個數組,要篩選出對應category
為html&css
的書籍。
const books = [ {name:'gitbook',category:'git'}, {name:'reactbook',category:'react'}, {name:'vuebook',category:'vue'}, {name:'cssbook',category:'html&css'}, {name:'htmlbook',category:'html&css'}, ] 複製程式碼
傳統的寫法是這樣:
let html_css_books = [] for (let i = 0; i < books.length; i++) { if(books[i].category === 'html&css'){ html_css_books.push(books[i]) } } console.log(html_css_books) 複製程式碼
我相信幾乎沒有人會選擇上面的方式,大部分人都會用 filter
const html_css_books = books.filter(function(item){return item.category === 'html&css'}) 複製程式碼
當然我們還可以用箭頭函式來縮減一些程式碼:
const html_css_books = books.filter(item => item.category === 'html&css') 複製程式碼
我知道這是一個大家都明白的例子,從這裡你能看到幾個高階函式的好處?
- 更短的程式碼
- 更少的錯誤
- 更多的複用
第三點你可能不同意,因為你可能會說,我們沒有複用任何程式碼啊?但如果我們把傳入的filter的回撥函式抽離出來呢?因為真正決定要過濾哪些資料的是這個部分。
const is_html_css_books = item => item.category === 'html&css' const is_git_books = item => item.category === 'git' const is_not_git_books = item => item.category !== 'git' const html_css_books = books.filter(is_html_css_books) const git_books = books.filter(is_git_books) const not_git_books = books.filter(is_not_git_books) 複製程式碼
清晰又簡潔不是嗎?
filter & map & find & reduce
這些都是我們常見的高階函式,但是它們的用法各不相同
函式 | 返回值 |
---|---|
filter | 大陣列 => 小陣列 |
map | 陣列 => 長度相等的陣列 |
find | 陣列 => 單個元素 |
reduce | 陣列 => 大陣列/小陣列/單個元素/長度相等的陣列/字串/Number/其他值 |
reduce
有很多玩法,甚至它可以取代我們剛剛說的三種高階函式,下一篇我們會聊聊reduce
的內容。接下來我們看看,高階函式有可能會遇到的問題,又如何去解決。
問題 & 解決
問題
我們一起來看這樣一個場景
比如我們需要計算 a, b 兩個值的和的兩倍再加3,我們可能會定義兩個函式
function double(a, b) { return (a + b) * 2 } function add3(a) { return a + 3 } 複製程式碼
那麼我們會這樣呼叫:
add3(double(1,3)) 複製程式碼
但是如果我們需要多加幾次3呢?
add3(add3(add3(add3(add3(double(1,3)))))) 複製程式碼
是的,雖然計算沒有錯誤,但是我們的可讀性大大降低了,那面對這樣的情況如何處理呢?
解決:鏈式優化
解決巢狀的第一種方法,就是拆解巢狀,鏈式呼叫,就像一條鏈子一樣,一環套一環,將上次的結果,作為下次的引數。
const chainObj = { double(a,b) { this.temp = (a + b) * 2; return this; }, add3() { this.temp += 3; return this; }, getValue() { const value = this.temp; // 記得這裡要初始化temp值 this.temp = undefined; return value; } }; 複製程式碼
所以我們上面的巢狀現在可以這樣寫:
chainObj.double().add3().add3().add3().add3().getValue() 複製程式碼
Promise
上節的這段程式碼
chainObj.double().add3().add3().add3().add3().getValue() 複製程式碼
對比 Promise 的程式碼
promise.then(fn).then(fn)... 複製程式碼
是不是很像呢?是的沒錯,我們平時寫的promise
其實都是在處理我們的高階函式
的執行順序。
那麼Promise
又是如何實現這樣的鏈式呼叫的呢?下一次和大家分享~