为了更好、更加深入的理解回调函数(callback) --->基于回调函数封装一个咱们本身的 _each 方法javascript
此方法 将 for each / map / jQuery中的each 三方法中的全部精华优势融合在一块儿,进行重写封装, 咱们本身的_each 此方法要比原有的强大不少java
//-> _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 没有被替换markdown
类数组
函数
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); 复制代码
对象
post
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); //-> 仍是原对象不发生改变 复制代码