一、forEach
1.语法和参数
我们可以先在MDN上查看forEach方法的语法和参数
2.基本使用方法
const arr = ['a', 'b', 'c', 'd', 'e'];
const obj = {name:"tom"}
arr.forEach(function (item, index, arr) {
console.log(item, index, arr, this);
},obj);
MDN文档中可以看到,forEach 接受两个参数:
- 一个回调函数,这个回调函数为数组中每个元素需要执行的函数
- 一个this指向值,可选参数
Array.prototype.myForEach= function(cb){
//myForEach方法是由数组调用:arr.forEach(...),所以当前方法下的this就是指向数组本身
const _arr = this;
//获取第二个参数,也就是需要改变的this指向值,这个参数是可选的,用于改变this指向
const _thisArg = arguments[1]||window;
}
拿到了外面传的参数(1.回调函数 2.需要改变的this指向值)和数组本身以后,我们只需要加上 myForEach 函数的处理逻辑就大功告成了,那么myForEach里面具体要做什么事情呢?其实很简单:就是循环遍历数组,让数组的每个元素执行一次回调函数就ok了
3.实现代码
Array.prototype.myForEach= function(cb){
//myForEach方法是由数组调用:arr.forEach(...),所以当前方法下的this就是指向数组本身
const _arr = this;
//获取第二个参数,也就是需要改变的this指向值,这个参数是可选的,用于改变this指向
const _thisArg = arguments[1]||window;
//循环遍历
for(var i = 0;i<_arr.length; i++){
//外面传入的回调函数里具体是有3个参数,分别是当前值,索引值,当前数组
//我们这里函数的调用需要借用apply来改变this指向
cb.apply(_thisArg, [_arr[i], i, _arr]);
}
//返回undefined
//return undefined
}
二、map
1.语法与参数
map方法的参数与语法跟forEach差别不大,区别在于map方法是有返回值的,返回值的类型为一个新数组. 我们可以在上面myForEach的基础上进行改造
Array.prototype.myMap = function (cb) {
//谁调用当前方法,this就是谁(当前数组)
var _arr = this;
var _length = _arr.length;
var _args2 = arguments[1] || window; //接受的第二个参数,可能有,可能没有.用于改变this指向
var _res; //如果没有任何返回,就是undefined
var result = [];
var _item;
for (var i = 0; i < _length; i++) {
_item = _arr[i];
//同样是使用apply函数来进行this指向的改变
_res = cb.apply(_args2, [_item, i, _arr]);
//将回调函数的返回值放入结果集中
result.push(_res);
}
return result;
};
上面的代码实现是有小瑕疵的,直接使用会出现我们常说的"浅拷贝"现象.详情请看下图控制台输出
我们把mapResult的第一项中的a值修改成100,原数组的第一项中的a值也会被影响
所以我们必须在代码中对数组中的值进行深拷贝处理,借助deepClone工具函数,deepClone代码我会放到文章最后
2.实现代码
Array.prototype.myMap = function (cb) {
//谁调用当前方法,this就是谁(当前数组)
var _arr = this;
var _length = _arr.length;
var _args2 = arguments[1] || window; //接受的第二个参数,可能有,可能没有.用于改变this指向
var _res; //如果没有任何返回,就是undefined
var result = [];
var _item;
for (var i = 0; i < _length; i++) {
_item = deepClone(_arr[i]); //小心对象引用的复制
_res = cb.apply(_args2, [_item, i, _arr]);
result.push(_res);
}
return result;
};
三、every
有了之前的基础,我们就不再赘述Every函数的用法和参数了,直接上代码.
Every方法的区别在于,所有数组项的回调函数结果返回为true,result才为true,否则则为false
Array.prototype.myEvery = function (cb) {
//谁调用当前方法,this就是谁(当前数组)
var _arr = this;
var _length = _arr.length;
var _args2 = arguments[1] || window; //接受的第二个参数,可能有,可能没有.用于改变this指向
var _res; //如果没有任何返回,就是undefined
var result = true;
var _item;
for (var i = 0; i < _length; i++) {
_item = deepClone(_arr[i]); //小心对象引用的复制
//有一个是false,结果则为false
if (!cb.apply(_args2, [_item, i, _arr])) {
return false;
}
}
return true;
};
四、some
some方法的区别在于,只要有一个数组项的回调函数结果返回true,result就为true,否则则为false
Array.prototype.mySome = function (cb) {
//谁调用当前方法,this就是谁(当前数组)
var _arr = this;
var _length = _arr.length;
var _args2 = arguments[1] || window; //接受的第二个参数,可能有,可能没有.用于改变this指向
var _res; //如果没有任何返回,就是undefined
var result = false;
var _item;
for (var i = 0; i < _length; i++) {
_item = deepClone(_arr[i]); //小心对象引用的复制
if (cb.apply(_args2, [_item, i, _arr])) {
//有一个是true,结果则为true
return true;
}
}
return result;
};
五、filter
filter方法的区别在于,只有数组项的回调函数结果返回true,我们才将它push进result中
Array.prototype.myFilter = function (cb) {
//谁调用当前方法,this就是谁(当前数组)
var _arr = this;
var _length = _arr.length;
var _args2 = arguments[1] || window; //接受的第二个参数,可能有,可能没有.用于改变this指向
var _res; //如果没有任何返回,就是undefined
var result = [];
var _item;
for (var i = 0; i < _length; i++) {
_item = deepClone(_arr[i]); //小心对象引用的复制
//cb的返回结果为true,我们才放入result中
cb.apply(_args2, [_item, i, _arr]) ? result.push(_item) : "";
}
return result;
};
六、reduce
在编写myReduce方法前,我们需要特别注意的是reduce接收的回调函数比其他几个方法接收的回调函数多了一个previousValue参数.
如上图所示,previousValue就是上一次调用回调函数的返回值,这个是最关键的点!!!
而当数组元素进行循环遍历执行回调函数后的结果就是这个previousValue
Array.prototype.myReduce = function (cb, initialValue) {
//谁调用当前方法,this就是谁(当前数组)
var _arr = this;
var _length = _arr.length;
var _args2 = arguments[2] || window; //接受的第三个参数,可能有,可能没有.用于改变this指向
var _item;
for (var i = 0; i < _length; i++) {
_item = deepClone(_arr[i]); //小心对象引用的复制
initialValue = cb.apply(_args2, [initialValue, _item, i, _arr]);
}
return initialValue;
};
七、reduceRight
reduceRight跟reduce的区别就在于讲数组的元素从右到左(从后到前)调用,我们只需要在myReduce的基础上修改遍历顺序即可.
Array.prototype.myReduceRight = function (cb, initialValue) {
//谁调用当前方法,this就是谁(当前数组)
var _arr = this;
var _length = _arr.length;
var _args2 = arguments[2] || window; //接受的第三个参数,可能有,可能没有.用于改变this指向
var _item;
for (var i = _length; i >= 0; i--) {
_item = deepClone(_arr[i]); //小心对象引用的复制
initialValue = cb.apply(_args2, [initialValue, _item, i, _arr]);
}
return initialValue;
};
八、总结
以上就是我们自己实现的Javascript数组原型对象上的七个方法,你学废了吗?
附上deepClone工具函数代码:
function deepClone(origin, target) {
var tar = target || {};
var toStr = Object.prototype.toString;
var arrayType = "[Object Array]";
for (var k in origin) {
if (origin.hasOwnProperty(k)) {
if (typeof origin[k] === "object" && origin[k] != null) {
tar[k] = toStr.call(origin[k]) === arrayType ? [] : {};
deepClone(origin[k], tar[k]);
} else {
tar[k] = origin[k];
}
}
}
return tar;
}