一:深拷贝
重写数组的 forEach、map、filter、every、some、reduce、reduceRight 七种方法。
因为有需要返回新数组的方法(map、filter等)。需要对数据进行深拷贝。用es5、es6的方式来实现深拷贝;
// es5的写法
function deepClone(origin, target) {
var tar = target || {};
var toStr = Object.prototype.toString;
var arrType = '[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]) === arrType ? [] : {};
deepClone(origin[k], tar[k])
} else {
tar[k] = origin[k]
}
}
}
return tar;
}
// es6的写法
function deepClone(origin, hashMap = new WeakMap()) {
// 如果是 null、undefined、原始值、function
if (origin == undefined || typeof origin !== 'object') {
return origin
}
if (origin instanceof Date) {
return new Date(origin)
}
if (origin instanceof RegExp) {
return new RegExp(origin)
}
// 如果 weakMap 中有此值,直接返回,不必在深拷贝。防止互相修改造成死循环
// let test1 = {}; let test2 = {}; test2.test1 = test1; test1.test2 = test2; deepClone(test2);
const hashkey = hashMap.get(origin);
if(hashkey){
return hashkey
}
// 对象 或 数组的情况
const target = new origin.constructor();
hashMap.set(origin, target);
for (var k in origin) {
if (origin.hasOwnProperty(k)) {
target[k] = deepClone(origin[k], hashMap);
}
}
return target;
}
二:方法重写
1. forEach()
forEach() 方法对数组的每个元素执行一次给定的函数。
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
callback:为数组中每个元素执行的函数,该函数接收三个参数:currentValue:数组中正在处理的当前元素。index可选 :数组中正在处理的当前元素的索引。array可选:forEach()方法正在操作的数组。
thisArg可选: 当执行回调函数callback时,用作this的值。如果不传默认是window。
Array.prototype.myForEach = function (cb) {
var _arr = this;
var _len = _arr.length;
// this 指向
var _arg2 = arguments[1] || window;
for (var i = 0; i < _len; i++) {
cb.apply(_arg2, [_arr[i], i, _arr]);
}
};
// 使用自己的 forEach 方法
var obj = {name: 'lay'};
[1,2,3].myForEach(function(item, index, array){
console.log(this.name); // lay
console.log(item, index, array)
// 1 0 [1,2,3]
// 2 1 [1,2,3]
// 3 2 [1,2,3]
}, obj);
2. map()
map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
arr.map(callback(currentValue [, index [, array]])[, thisArg])
callback:生成新数组元素的函数,该函数接收三个参数:currentValue:数组中正在处理的当前元素。index可选 :数组中正在处理的当前元素的索引。array可选:forEach()方法正在操作的数组。
thisArg可选: 当执行回调函数callback时,用作this的值。如果不传默认是window。
Array.prototype.myMap = function (cb) {
var _arr = this;
var _len = _arr.length;
var _arg2 = arguments[1] || window;
var _item;
var _res;
var _newArr = [];
for (var i = 0; i < _len; i++) {
_item = deepClone(_arr[i]);
_res = cb.apply(_arg2, [_item, i, _arr]);
_res && _newArr.push(_res)
}
return _newArr;
};
var obj = {name: 'lay'};
var ret = [1, 2, 3].myMap(function (item, index, array) {
console.log(this.name); // lay
return item * 2;
}, obj);
console.log(ret); // [2,4,6]
3. filter()
filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
arr.filter(callback(currentValue [, index [, array]])[, thisArg])
callback:用来测试数组的每个元素的函数。返回true表示该元素通过测试,保留该元素,false则不保留。该函数接收三个参数:currentValue:数组中正在处理的当前元素。index可选 :数组中正在处理的当前元素的索引。array可选:forEach()方法正在操作的数组。
thisArg可选: 当执行回调函数callback时,用作this的值。如果不传默认是window。
Array.prototype.myFilter = function (cb) {
var _arr = this;
var _len = _arr.length;
var _arg2 = arguments[1] || window;
var _item;
var _newArr = [];
for (var i = 0; i < _len; i++) {
_item = deepClone(_arr[i]);
cb.apply(_arg2, [_item, i, _arr]) ? _newArr.push(_item) : '';
}
return _newArr;
};
var obj = {name: 'lay'};
var ret = [1, 2, 3].myFilter(function (item, index, array) {
console.log(this.name); // lay
return item >= 2;
}, obj);
console.log(ret); // [2,3]
4. every()
every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
arr.every(callback(currentValue [, index [, array]])[, thisArg])
callback:用来测试每个元素的函数。该函数接收三个参数:currentValue:数组中正在处理的当前元素。index可选 :数组中正在处理的当前元素的索引。array可选:forEach()方法正在操作的数组。
thisArg可选: 当执行回调函数callback时,用作this的值。如果不传默认是window。
Array.prototype.myEvery = function (cb) {
var _arr = this;
var _len = _arr.length;
var _arg2 = arguments[1] || window;
var _res = true;
for (var i = 0; i < _len; i++) {
if(!cb.apply(_arg2, [_arr[i], i, _arr])){
_res = false;
break;
}
}
return _res;
};
var obj = {name: 'lay'};
var ret = [1, 2, 3].myEvery(function (item, index, array) {
console.log(this.name); // lay
return item >= 2;
}, obj);
console.log(ret); // false
5. some()
some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是布尔值。
arr.some(callback(currentValue [, index [, array]])[, thisArg])
callback:用来测试每个元素的函数。该函数接收三个参数:currentValue:数组中正在处理的当前元素。index可选 :数组中正在处理的当前元素的索引。array可选:forEach()方法正在操作的数组。
thisArg可选: 当执行回调函数callback时,用作this的值。如果不传默认是window。
Array.prototype.mySome = function (cb) {
var _arr = this;
var _len = _arr.length;
var _arg2 = arguments[1] || window;
var _res = false;
for (var i = 0; i < _len; i++) {
if(cb.apply(_arg2, [_arr[i], i, _arr])){
_res = true;
break;
}
}
return _res;
};
var obj = {name: 'lay'};
var ret = [1, 2, 3].mySome(function (item, index, array) {
console.log(this.name); // lay
return item >= 2;
}, obj);
console.log(ret); // true
6. reduce()
reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
-
callback:执行数组中每个值 (如果没有提供initialValue则第一个值除外)的函数,包含四个参数:-
accumulator:累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。 -
currentValue:数组中正在处理的元素。 -
index可选:数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。 -
array可选:调用reduce()的数组
-
-
initialValue可选:作为第一次调用callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
Array.prototype.myReduce = function (cb) {
var _arr = this;
var _len = _arr.length;
// 在空数组上调用 reduce 且未提供初始值 报类型错误
if(!_len && !arguments[1]){
return new TypeError('Reduce of empty array with no initial value');
}
var _initialValue = arguments[1] || _arr[0];
// 如果传了initialValue 就从第0位开始,如果没传从第1位开始;
var i = arguments[1] ? 0 : 1;
var _item;
for (i; i < _len; i++) {
_item = deepClone(_arr[i]);
_initialValue = cb.apply(null, [_initialValue, _item, i, _arr]);
}
return _initialValue;
};
var ret = [1,2,3,4].myReduce(function(prev, item, index, array){
return prev + item;
}, 1);
console.log(ret); // 11
var ret1 = [1,2,3,4].myReduce(function(prev, item, index, array){
return prev + item;
});
console.log(ret1); // 10
var ret2 = [].myReduce(function(prev, item, index, array){
return prev + item;
});
console.log(ret2); // 报TypeError
7. reduceRight()
reduceRight() 方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将结果汇总为单个返回值。
-
callback:一个回调函数,用于操作数组中的每个元素,它可接受四个参数:-
accumulator:累加器:上一次调用回调函数时,回调函数返回的值。首次调用回调函数时,如果initialValue存在,累加器即为initialValue,否则须为数组中的最后一个元素。 -
currentValue:当前元素:当前被处理的元素。 -
index可选:数组中当前被处理的元素的索引。 -
array可选:调用reduceRight()的数组。
-
-
initialValue可选 :首次调用callback函数时,累加器accumulator的值。如果未提供该初始值,则将使用数组中的最后一个元素,并跳过该元素。如果不给出初始值,则需保证数组不为空。否则,在空数组上调用reduce或reduceRight且未提供初始值(例如[].reduce( (acc, cur, idx, arr) => {} ))的话,会导致类型错误TypeError: reduce of empty array with no initial value。
Array.prototype.myReduceRight = function (cb) {
var _arr = this;
var _len = _arr.length;
// 在空数组上调用 reduceRight 且未提供初始值 报类型错误
if (!_len && !arguments[1]) {
return new TypeError('Reduce of empty array with no initial value');
}
var _initialValue = arguments[1] || _arr[_len - 1];
// 如果传了initialValue 就从第最后一位索引开始,如果没传从第倒数第一位索引开始;
var i = arguments[1] ? _len - 1 : _len - 2;
var _item;
for (i; i >= 0; i--) {
_item = deepClone(_arr[i]);
_initialValue = cb.apply(null, [_initialValue, _item, i, _arr]);
}
return _initialValue;
};
var ret = [1,2,3,4].myReduceRight(function(prev, item, index, array){
return prev + item;
}, 1);
console.log(ret); // 11
var ret1 = [1,2,3,4].myReduceRight(function(prev, item, index, array){
return prev + item;
});
console.log(ret1); // 10
var ret2 = [].myReduceRight(function(prev, item, index, array){
return prev + item;
});
console.log(ret2); // 报TypeError