[前端漫談_3] 從 filter 聊到 Promise

dendoink發表於2019-03-20

前言

在學習前端的時候,我總是能聽到很多高階詞彙,比如今天會聊到的 函數語言程式設計(Functional Programming) & 高階函式 (Higher-order function) 。 但是當你真正的理解什麼是 函數語言程式設計 & 高階函式 的時候,也許會發現,你幾乎每天都會用到它,只是你不知道那就是高階函式 / 函數語言程式設計。

JavaScript 中的函式

javascript 中,函式是一種值,舉個例子:

const double = function (x) {
  return x * 2
}
複製程式碼

我們把一個函式作為值,賦給了變數 double ,這在我們的程式碼中很常見對嗎?

你是不是經常會聽到或者看到這樣一句話:“在 JavaScript 中函式是一等公民”

粗看很不好理解,但是它的意思很簡單:函式和 字串/number 沒有什麼不一樣,它可以宣告為變數,也可以作為引數傳入到其他函式中。

什麼是高階函式?

什麼是高階函式?其實上一段我們已經說過了,我們可以把函式A作為引數傳入到另一個函式B中,那麼接收函式作為引數的函式B,就是 高階函式 ,這只是方便大家理解,高階函式的定義是:

"一個函式的引數是另一個函式,或者一個函式的返回值是另一個函式"

高階函式的例子

filter

說到 filter() 你肯定不陌生,他接收一個回撥函式作為它的引數,所以它是一個典型的高階函式,舉個例子:

我們有這麼一個陣列,要篩選出對應 categoryhtml&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 除錯技巧 已經開始預售啦。

歡迎關注公眾號 「前端惡霸」,掃碼關注,會有很多好東西等著你~

[前端漫談_3] 從 filter 聊到 Promise

相關文章