迭代器在不少語言都很常見,js 的 forEach 就是一個迭代器,下面就來介紹實現一個支持數組、對象、類數組的的 each 函數。node
寫以前先整理一下思路git
判斷基本類型咱們可使用 typeof
,不過對於引用類型,好比數組和函數都會返回object
,那麼還有更好的判斷方法麼? 那就是Object.prototype.toString
github
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, 2:45, 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
length
屬性length
爲 0 的狀況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
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;
}
}
}
}
複製代碼