迭代器在不少语言都很常见,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;
}
}
}
}
复制代码