讀Zepto原始碼之IOS3模組

對角另一面發表於2017-09-30

IOS3 模組是針對 IOS 的相容模組,實現了兩個常用方法的相容,這兩個方法分別是 trimreduce

讀 Zepto 原始碼系列文章已經放到了github上,歡迎star: reading-zepto

原始碼版本

本文閱讀的原始碼為 zepto1.2.0

GitBook

reading-zepto

trim

if (String.prototype.trim === undefined) // fix for iOS 3.2
  String.prototype.trim = function(){ return this.replace(/^s+|s+$/g, ``) }

看註釋, trim 是為了相容 ios3.2 的。

也是常規的做法,如果 Stringprototype 上沒有 trim 方法,則自己實現一個。

實現的方式也簡單,就是用正則將開頭和結尾的空格去掉。^s+ 這段是匹配開頭的空格,s+$ 是匹配結尾的空格。

reduce

// For iOS 3.x
// from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
if (Array.prototype.reduce === undefined)
  Array.prototype.reduce = function(fun){
    if(this === void 0 || this === null) throw new TypeError()
    var t = Object(this), len = t.length >>> 0, k = 0, accumulator
    if(typeof fun != `function`) throw new TypeError()
    if(len == 0 && arguments.length == 1) throw new TypeError()

    if(arguments.length >= 2)
      accumulator = arguments[1]
    else
      do{
        if(k in t){
          accumulator = t[k++]
          break
        }
        if(++k >= len) throw new TypeError()
      } while (true)

    while (k < len){
      if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t)
      k++
    }
    return accumulator
  }

用法與引數

要理解這段程式碼,先來看一下 reduce 的用法和引數:

用法

arr.reduce(callback[, initialValue])

引數

  • callback: 回撥函式,有如下引數

    • accumulator: 上一個回撥函式返回的值或者是初始值(initialValue
    • currentValue: 當前值
    • currentIndex: 當前值在陣列中的索引
    • array: 呼叫 reduce 的陣列
  • initialValue: 初始值,如果沒有提供,則為陣列的第一項。如果陣列為空陣列,而又沒有提供初始值時,會報錯

檢測引數

if(this === void 0 || this === null) throw new TypeError()
var t = Object(this), len = t.length >>> 0, k = 0, accumulator
if(typeof fun != `function`) throw new TypeError()
if(len == 0 && arguments.length == 1) throw new TypeError()

首先檢測是否為 undefined 或者 null ,如果是,則報型別錯誤。這裡有一點值得注意的,判斷是否為 undefined 時,用了 void 0 的返回值,因為 void 操作符返回的結果都為 undefined ,這是為了避免 undefined 被重新賦值,出現誤判的情況。

接下來,將陣列轉換成物件,用變數 t 來儲存,後面會看到,遍歷用的是 for...in 來處理。為什麼不直接用 for 來處理陣列呢?因為 reduce 不會處理稀疏陣列,所以轉換要轉換成物件來處理。

陣列長度用 len 來儲存,這裡使用了無符號位右移操作符 >>> ,確保 len 為非負整數。

k 來儲存當前索引,accumulator 為返回值。

接下來,檢測回撥函式 fun 是否為 function ,如果不是,丟擲型別錯誤。

在陣列為空,並且又沒有提供初始值(即只有一個引數 fun)時,丟擲型別錯誤。

accumulator初始值

if(arguments.length >= 2)
  accumulator = arguments[1]
else
  do{
    if(k in t){
      accumulator = t[k++]
      break
    }
    if(++k >= len) throw new TypeError()
  } while (true)

如果引數至少有兩項,則 accumulator 的初始值很簡單,就是 arguments[1] ,即 initialValue

如果沒有提供初始值,則迭代索引,直到找到在物件 t 中存在的索引。注意這裡用了 do...while,所以最終結果,要麼是報型別錯誤,要麼 accumulator 能獲取到值。

這段還巧妙地用了 ++kk++ 。如果 k 在物件 t 中存在時,則賦值給 accumulatork 再自增,否則用 k 自增後再和 len 比較,如果超出 len 的長度,則報錯,因為不存在下一個可以賦給 accumulator 的值。

返回結果

while (k < len){
  if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t)
  k++
}
return accumulator

要注意,如果沒有提供初始值時,k 是自增後的值,即不再需要處理陣列的第一個值。

到這裡問題就比較簡單了,就是 while 迴圈,用 accumulator 儲存回撥函式返回的值,在下一次迴圈時,再將 accumulator 作為引數傳遞給回撥函式,直至陣列耗盡,然後將結果返回。

系列文章

reading-zepto

系列文章

  1. 讀Zepto原始碼之程式碼結構
  2. 讀Zepto原始碼之內部方法
  3. 讀Zepto原始碼之工具函式
  4. 讀Zepto原始碼之神奇的$
  5. 讀Zepto原始碼之集合操作
  6. 讀Zepto原始碼之集合元素查詢
  7. 讀Zepto原始碼之操作DOM
  8. 讀Zepto原始碼之樣式操作
  9. 讀Zepto原始碼之屬性操作
  10. 讀Zepto原始碼之Event模組
  11. 讀Zepto原始碼之IE模組
  12. 讀Zepto原始碼之Callbacks模組
  13. 讀Zepto原始碼之Deferred模組
  14. 讀Zepto原始碼之Ajax模組
  15. 讀Zepto原始碼之Assets模組
  16. 讀Zepto原始碼之Selector模組
  17. 讀Zepto原始碼之Touch模組
  18. 讀Zepto原始碼之Gesture模組

附文

參考

License

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

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

作者:對角另一面

相關文章