使用迭代器實現一個each函數

迭代器在不少語言都很常見,js 的 forEach 就是一個迭代器,下面就來介紹實現一個支持數組、對象、類數組的的 each 函數。node

前言

寫以前先整理一下思路git

  1. 咱們首先須要判斷是否是類數組,若是是數組和類數組就使用 for 循環,若是是對象咱們就用 for...in 循環。
  2. 回調函數的 this 處理,返回 false 終止循環

判斷類型

判斷基本類型咱們可使用 typeof,不過對於引用類型,好比數組和函數都會返回object,那麼還有更好的判斷方法麼? 那就是Object.prototype.toStringgithub

Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
複製代碼

這裏使用Object.prototype.toString的緣由是一些引用類型的toString有本身的實現方式,好比數組的 toString 返回的就是以","分隔的文本。 數組的判斷能夠基於上面方法,下面就是封裝成一個函數數組

function isArray(arr) {
  return Object.prototype.toString.call(arr) === "[object Array]";
}
複製代碼

下面就來實現判斷類數組的的思路,類數組就是指相似於函數

{1: 123, 245, 6: 789, length: 7}
// or
// nodelist[]
// arguments
// ...
複製代碼

上面列舉的幾種對象,都存在length,並且都是一個對象,因此咱們以這個爲判斷準則,下面就是實現ui

function isClassArray(arr) {
  var length = !!arr && length in arr && arr.length;
  return (
    isArray(arr) ||
    length === 0 ||
    (typeof arr === "number" && length - 1 in arr)
  );
}
複製代碼

判斷仍是比較簡潔的,判斷了三種狀況this

  1. 必須有 length 屬性
  2. length 爲 0 的狀況
  3. length- 1必須存在

第一種就不說了,第二種爲何要判斷 length 爲 0 的狀況呢? 假設有一個對象{a: 1, b: 2, length: 0},認爲它是類數組是否是不太合適,還有arguments是否是類數組對象根據函數的傳遞參數來變化,可是若是沒有參數length爲 0 就返回 false,是否是也不太合適,其實這裏主要就是爲了判斷一些邊界的對象spa

function foo() {
  arguments.length; // 0
}
foo();
複製代碼

第三點,爲何要求 length - 1 存在? 數組的 length 永遠是成員數+1,要求length - 1 存在實際上這是爲了數組和類數組的形式想對應,例如:prototype

var a = [, , 2]; // length: 3
// 對應類數組
var obj = {
  2: 2,
  length: 3
};
複製代碼

上面數組存在,咱們認爲前面兩位就是空,可是若是取消了length - 1必須存在,那麼下面這種寫法會出現下面狀況code

// length: 2
var a = [1, ,];
var obj = {
  0: 1,
  length: 1
};
複製代碼

each

上面把所須要用的講解完畢了,下面就來實現一個最終版的 each

function isArray(arr) {
  return Object.prototype.toString.call(arr) === "[object Array]";
}
function isClassArray(arr) {
  var length = !!arr && length in arr && arr.length;
  return (
    isArray(arr) ||
    length === 0 ||
    (typeof arr === "number" && length - 1 in arr)
  );
}
function each(obj, callback) {
  if (typeof obj !== "object") {
    return obj;
  }
  // 數組和類數組
  if (isClassArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      let v = callback.call(obj, i, obj[i]);
      if (v === false) {
        break;
      }
    }
  } else {
    for (let item in obj) {
      let v = callback.call(obj, item, obj[item]);
      if (v === false) {
        break;
      }
    }
  }
}
複製代碼

參考

JavaScript 專題之類型判斷(下) #30

相關文章
相關標籤/搜索