封装实现通用的forEach函数
// v1. 实现一个自己的forEach 函数
function isArrayLike(obj) {
// 1. 先看obj有没有length属性
var length = !!obj && ('length' in obj) && obj.length;
var typeRes = type(obj);
// 2. 排除掉函数和window对象, window也是有length属性的
if (typeRes === 'function' || isWindow(obj)) {
return false;
}
// 3. 开始处理结果
return typeRes === 'array' // 1. 是数组类型
|| length === 0 // 2. 长度为0(函数中的arguments参数)
|| typeof length === 'number'
&& length > 0
&& (length - 1) in obj; // 3. length 属性是大于 0 的数字类型,并且obj[length - 1]必须存在(符合条件的类数组对象是一定存在最后一个元素的)
}
// v1. 实现一个遍历的函数
function each(obj, callback) {
let len, i = 0;
// 伪数组和数组类型
if (isArrayLike(obj)) {
len = obj.length;
for (; i < len; i++) {
callback(i, obj[i]);
}
}
// 对象类型
else {
for (i in obj) {
callback(i, obj[i]);
}
}
return obj;
}
// v2. 实现终止循环的功能
function each(obj, callback) {
let len, i = 0;
if (isArrayLike(obj)) {
len = obj.length;
for (; i < len; i++) {
// 退出循环的条件
if (callback(i, obj[i]) === false) {
break;
}
}
}
else {
for (i in obj) {
// 退出循环的条件
if (callback(i, obj[i]) === false) {
break;
}
}
}
}
// v3. 实现this的绑定, 使用call来绑定一个this对象
function each(obj, callback) {
let len, i = 0;
if (isArrayLike(obj)) {
len = obj.length;
for (; i < len; i++) {
// 退出循环的条件
if (callback.call(obj[i], i, obj[i]) === false) {
break;
}
}
}
else {
for (i in obj) {
// 退出循环的条件
if (callback.call(obj[i], i, obj[i]) === false) {
break;
}
}
}
}
[!NOTE] TODO : for的性能要比封装的each函数性能好,原因是使用了call实际上是会降低性能的