基于回调函数封装一个强大的_each

795 阅读4分钟

为了更好、更加深刻的理解回调函数(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, 203040] ;
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); 
		//-> 还是原对象不发生改变