lodash原始碼分析之baseFindIndex中的運算子優先順序

对角另一面發表於2018-02-05
我悟出權力本來就是不講理的——蟑螂就是海米;也悟出要造反,內心必須強大到足以承受任何後果才行。

——北島《城門開》

本文為讀 lodash 原始碼的第十篇,後續文章會更新到這個倉庫中,歡迎 star:pocket-lodash

gitbook也會同步倉庫的更新,gitbook地址:pocket-lodash

作用與用法

baseFindIndex 是內部方法,其作用類似於ES6的 findIndex,查詢符合條件的第一個元素的索引。

baseFindIndex 除了從前向後查詢外,還可以從後向前查詢。

用法如下:

baseFindIndex([3,1,2], function(val, index, array) {
  return val > 1
}, 1) // 從前向後查詢,從索引1開始查詢,返回2
baseFindIndex([3,1,2], function(val, index, array) {
  return val > 1
}, 1, true) // 從後向前查詢,從索引1開始查詢,返回3

原始碼分析

function baseFindIndex(array, predicate, fromIndex, fromRight) {
  const { length } = array
  let index = fromIndex + (fromRight ? 1 : -1)

  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index
    }
  }
  return -1
}

這段程式碼再次展示了 lodash 的特點,短小精悍!

這次讀原始碼我們從裡往外看。

從程式碼中很容易看到,predicate 是傳遞進來的函式,在 baseFindIndex 呼叫該函式,如果返回的結果為真值,則中止查詢,返回索引。

運算子優先順序

我們再往外看,看看 while 的迴圈條件:

fromRight ? index-- : ++index < length

現在問題來了,這個三元表示式有兩種可能,一種是:

(fromRight ? index-- : ++index) < length

一種是:

fromRight ? index-- : (++index < length)

究竟是那一種呢?這就要看運算子的最佳化級了,下面這個表是 MDN 上的截圖:

這個表將最佳化級劃分成了20個級別,數字越大,最佳化級越高。

從表中可以看到,比較運算子的優先順序為11,而三元表示式(條件運算子)的最佳化級為4,因此可以確定比較運算子的優先順序要比三元表示式的要高,迴圈條件其實等價於第二種寫法。

增減迷局

再往上看,可以看到這句程式碼:

let index = fromIndex + (fromRight ? 1 : -1)

在向後查詢時, index 減少了1,而向前查詢時,index 增加了1,為什麼要這樣做呢?

再結合迴圈條件看下:

fromRight ? index-- : ++index < length

在向前查詢時,使用的是 index-- 表示式的運算結果,向後查詢時,使用的是 ++index < lenth 表示式的運算結果。

從上表中也可以看出字首自增比比較運算子的最佳化級要高。

字首自增返回的是自增後的結果,而在迴圈條件中就要將索引 index 增加1,這樣會忽略掉第一個需要遍歷的元素,作為補救,在開始遍歷前,需要將 index 減少1。

同理,在向前查詢時,需要將索引增加1,因為在遍歷開始時就已經將索引減少1。

那又為什麼向前查詢時用的是字尾自減,而不是用字首自減呢?

因為在向前查詢時,最終要查詢到陣列索引 0 的位置,字尾自減返回的是自減前的數值,因此當 index1 時,自減後的 index0 ,但是在迴圈條件中依然拿 1 來進行判斷,所以使得索引 0 得以進入迴圈體。

關於字首自增/減和字尾自增/減的區別可以看《lodash原始碼分析之自減的兩種形式》。

參考

  1. MDN:運算子優先順序

License

署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)

最後,所有文章都會同步傳送到微信公眾號上,歡迎關注,歡迎提意見:

作者:對角另一面

相關文章