前言
在學習前端的時候,我總是能聽到很多高階詞彙,比如今天會聊到的 函數語言程式設計(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
又是如何實現這樣的鏈式呼叫的呢?下一次和大家分享~
最後
小冊 你不知道的 Chrome 除錯技巧 已經開始預售啦。
歡迎關注公眾號 「前端惡霸」,掃碼關注,會有很多好東西等著你~