JavaScript 實現 對象、數組、類數組通用的遍歷方法 each

jQuery 的 each 方法

jQuery 有個 each 方法,是對於對象、數組通用的遍歷方法。

具體的用法參考官方文檔:.each()

今天我們也來實現這樣一個 each 方法。

思路

參數

參考 jQuery ,each 方法接收兩個參數:

  • 要遍歷的對象;
  • 要在其上執行的回調函數。

邏輯:

  • 自然是要先判斷其類型,是數組(類數組)就執行 for 循環(forEach 等不合適,後面再說),是對象就執行 for in 循環;
  • 循環中執行回調函數。

關於數組、類數組的判斷:

  • 類數組是擁有一個 length 屬性和 若干索引屬性 的對象,常見的 arguments 對象就是類數組;
  • 類數組不是數組, Array.prototype 上的方法無法在類數組對象上調用,會報錯;
  • 類數組的條件更“寬”,即符合數組特徵的一定符合類數組特徵,反之不一定,而它們的遍歷方法一致。所以在數組、類數組通用的遍歷方法 each 的判斷邏輯中,可以只劃一條“及格線”,只需要檢測是否是類數組即可。

參考 JavaScript 權威指南中給出的方法:

function isArrayLike(o) {
    if (o && // o is not null, undefined, etc
        // o is an object
        typeof o === "object" &&
        // o.length is a finite number
        isFinite(o.length) &&
        // o.length is non-negative
        o.length >= 0 &&
        // o.length is an integer
        o.length === Math.floor(o.length) &&
        // o.length < 2^32
        o.length < 4294967296) //數組的上限值
        return true;
    else 
        return false;
}

代碼

基本款

由此,得到以下代碼:

// 基本款
function each(obj, cb) {
  var length, i = 0;
  if ( isArrayLike(obj) ) {
      length = obj.length;
      for ( ; i < length; i++ ) {
          // 此處如果用 forEach 遍歷,注意參數的順序
          cb(i, obj[i])
      }
  } else {
      for ( i in obj ) {
          cb(i, obj[i])
      }
  }
  return obj;
}

注: 此處直接用 forEach 遍歷是可以的,但要注意 forEach 的回調函數的參數的順序。

修復 this

遍歷過程中,爲了讓回調函數中的 this 指向元素,我們使用 call() 方法:

// call() 方法第一個參數即爲 this 指向的 context
callbackFunction.call(obj[i]i, obj[i])

代碼:

// 修復 this
function each(obj, cb) {
  var length, i = 0;
  if ( isArrayLike(obj) ) {
      length = obj.length;
      for ( ; i < length; i++ ) {
          // 使用 call()
          cb.call(obj[i],i, obj[i])
      }
  } else {
      for ( i in obj ) {
          // 使用 call()
          cb.call(obj[i],i, obj[i])
      }
  }
  return obj;
}

終止遍歷的功能

jQuery 的 each 方法有終止遍歷的功能:

  • 當回調函數返回值爲 false,則終止遍歷。

思路:
這個實現起來很簡單,由於我們使用了 for 循環,可以在檢測到 false 時,通過 break 輕鬆實現終止遍歷。

if (callbackFunction.call(obj[i], i, obj[i]) === false) {
    break;
}

用上面這段代碼替換

callbackFunction.call(obj[i], i, obj[i])

最終代碼:

// 最終代碼
function each(obj, cb) {
  var length, i = 0;
  if ( isArrayLike(obj) ) {
      length = obj.length;
      for ( ; i < length; i++ ) {
          // 終止遍歷的功能
          if (cb.call(obj[i], i, obj[i]) === false) {
               break;
          }
      }
  } else {
      for ( i in obj ) {
          // 終止遍歷的功能
          if (cb.call(obj[i], i, obj[i]) === false) {
                break;
          }
      }
  }
  return obj;
}

測試

let obj = {
  ak:"av",
  bk:"bv"
}

let array = [1,2,3]

let arr = [{jb:'jb1'},{jw: 'jw1'}]

function logger (index, item) {
  console.log(`~ ${index}: ${item} ~`);
}

each(obj, logger)
each(array, logger)
each(arr, logger)

輸出:

~ ak: av ~
~ bk: bv ~
~ 0: 1 ~
~ 1: 2 ~
~ 2: 3 ~
~ 0: [object Object] ~
~ 1: [object Object] ~

成功~~

參考:
https://github.com/mqyqingfeng/Blog/issues/40
https://blog.csdn.net/beijiyang999/article/details/79760562
https://blog.csdn.net/beijiyang999/article/details/80179097

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章