重写数组方法

730 阅读4分钟

一:深拷贝

重写数组的 forEachmapfiltereverysomereducereduceRight 七种方法。

因为有需要返回新数组的方法(mapfilter等)。需要对数据进行深拷贝。用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