为了更好、更加深刻的理解回调函数(callback) --->基于回调函数封装一个我们自己的 _each 方法
此方法 将 for each / map / jQuery中的each 三方法中的所有精华优点融合在一起,进行重写封装, 我们自己的_each 此方法要比原有的强大很多
//-> _type 自己封装的强大的 数据类型 方法 在我的文章有专题讲解 下面是链接
https://juejin.im/post/5d6dc9b2f265da03b9500f75
var _obj = {
isNumeric: "Number",
isBoolean: 'Boolean',
isString: 'String',
isNull: 'Null',
isUndefined: 'Undefined',
isSymbol: 'Symbol',
isPlainObject: 'Object',
isArray: 'Array',
isRegExp: 'RegExp',
isDate: 'Date',
isFunction: "Function",
isWindow: 'Window'
},
_toString = _obj.toString,
_type = {};
for (var key in _obj) {
if (!_obj.hasOwnProperty(key)) break;
_type[key] = (function () {
var reg = new RegExp("^\\[object " + _obj[key] + "\\]$");
return function anonymous(val) {
return reg.test(_toString.call(val));
}
})();
}
/* 基于ES6 新语法进行封装
---> 基于我们自己封装的数据类型检测方法 _type 来实现检测 <----
* _each:遍历数组、类数组、对象中的每一项
* @params
* obj:需要迭代的数组、类数组、普通对象
* callback:回调函数(每遍历数组中的某一项,就会把回调函数执行一次;
* 而且需要把当前遍历的内容和索引[属性值和属性名]传给回调函数;* 接收回调函数的返回结果,如果是false,则结束当前的循环;
* 如果是其它值,让返回的值替换数组中的当前项;
* 如果没有返回值,则什么都不处理...)
* 支持第三个参数:
* context:传递的第三个参数,可以改变回调函数中的THIS指向,不传 * 递默认是window 可以设置形参默认值 context = window
* @return
* 返回一个新的数组或者对象(原来的数组或者对象不变)
*/
//-> 逐行解析
function _each(obj, callback, context = window) {
/*
* 首先判断 obj 是不是数组 或者 (obj里存在length属性 并且它的length 属性是一个数字 )
* 当 符合以上两个条件时 _type.isArray(obj) --> 这里说明是一个数组 || (('length' in obj) && _type.isNumeric(obj.length))--> 这里说明不是一个数组 但是他有length属性,并且属性值是数字, 说明他是一个类数组
*
* 类数组 和 数组 都符合 isLikeArray 条件
*/
let isLikeArray = _type.isArray(obj) || (('length' in obj) && _type.isNumeric(obj.length));
/*
* 每循环一次就会把 callback 执行一次 ,所以执行时看callback是不是一个函数
* 检测 当 callback 不等于一个函数时就它等于一个 匿名空函数 否则什么都不做
*
* 扫盲: Function.prototype() -> 匿名空函数 就是一个匿名空函数,执行时啥效果也没有
*/
typeof callback !== "function" ? callback = Function.prototype : null;
//=>数组或者类数组
/*
* 如果 isLikeArray 是数组或者类数组
* for循环遍历 每循环一次 obj 都会把 callback 执行一次
* callback()不仅要执行 还要将里面的 this 改为第三参数 context ,还要把 数组循环的当前项item 和 索引i 传给callback() result接收返回值
* 如果 result 接受的返回值为 false 直接 break 循环结束
* 如果 result 的值为 "undefined" 结束当前,继续下轮循环
* 当 返回值的值既不是false 也不是 undefined 也有返回值了 我们要让当前的result 返回值替换 数组中的当前项 arr[i] = result;
* 最后把克隆后的新数组并且经过替换的 返回
*/
if (isLikeArray) {
//-> 将原数组克隆一份
let arr = [...obj];
for (let i = 0; i < arr.length; i++) {
let item = arr[i],
result = callback.call(context, item, i);
if (result === false) break;
if (typeof result === "undefined") continue;
arr[i] = result;
}
return arr;
}
//=>对象的处理
/*
* 首先 把对象解构赋值 克隆一份 给 opp 后面都对opp进行操作 防止改变原有队象的
* 基于 for in 循环 opp 对象
* 如果能进入循环的话 首先判断是不是自己私有属性 不是就直接结束循环 break 只循环遍历自己私有的
* 定义 value 等于 opp[key] 拿到属性值
result 接收 callback 执行的返回结果 把this 改变 把 value key 传入
* 如果 result 接受的返回值为 false 直接 break 循环结束
* 如果 result 的值为 "undefined" 结束当前,继续下轮循环
* 当 返回值的值既不是false 也不是 undefined 也有返回值了 我们要让当前的result 返回值替换 数组中的当前项 opp[key] = result;
* 最后把克隆的 OPP 并且经过替换的 返回
*/
let opp = {
...obj
};
for (let key in opp) {
if (!opp.hasOwnProperty(key)) break;
let value = opp[key],
result = callback.call(context, value, key);
if (result === false) break;
if (typeof result === "undefined") continue;
opp[key] = result;
}
return opp;
}
写了这么久, 我们运行一下看看效果
数组
let arr = [10, 20,30,40] ;
let arr2 =_ each(arr, (item,index) => {
console.log(item, index);
if (index >= 2) return false;
return item * 10; //-> 函数执行的返回值会把新数组中的进行替换掉
});
console.log(arr, arr2);
上面的 【100,200,30,40】 30 没有被替换原因 --> if (index >= 2) return false; 当大于等2 的时候 后面的代码不会被执行 所以30 没有被替换
类数组
function func() {
let arr = _each(arguments, (item, index) => {
console.log(item, index);
if (index >= 2) return false;
return item * 10;
});
console.log(arguments, arr);
}
func(10, 20, 30, 40);
对象
let obj = {
name: '珠峰',
year: 10,
teacher: '哇咔咔~'
};
let obj2 = _each(obj, function (value, key) {
// console.log(this); //=>document
// console.log(value, key);
if (key === "name") {
return "珠峰培训@zhufeng";
}
}, document);
console.log(obj, obj2);
//-> 还是原对象不发生改变