数组polyfill

Array.from( )

  1. Array.myFrom = (function () {
  2. // 判断参数是否可调用,也就是判断value是不是函数。
  3. var isCallable = function (fn) {
  4. return typeof fn === 'function' || Object.prototype.toString.call(fn) === '[object Function]';
  5. };
  6. // 因为需要使用类数组中的length属性,我们需要检查length的值是不是整数,不是整数情况下需要转化成整数。
  7. // 也就是判断value是不是数字
  8. var toInteger = function (value) {
  9. var num = Number(value);
  10. if (isNaN(num)) { return 0 };
  11. if (num === 0 || !isFinite(num)) { return num };
  12. return (num > 0 ? 1 : -1) * Math.floor(Math.abs(num)); //变成标准的正整数和负整数。
  13. }
  14. // 上面一个函数使得变成标准的正整数和负整数,但是length的数值不能是负数和无限大的数字。
  15. var toLength = function (value) {
  16. var maxSafeInteger = Number.MAX_SAFE_INTEGER,
  17. len = toInteger(value);
  18. return Math.min(Math.max(len, 0), maxSafeInteger);
  19. }
  20. return function from(arrayLikeOrIterator) {
  21. const caller = this, //保存调用方
  22. item = Object(arrayLikeOrIterator), //参数1包装成对象
  23. isIterator = isCallable(item[Symbol.iterator]); //判断对象是否可迭代
  24. // 检查参数1是不是类数组、可迭代对象
  25. if (!isIterator && arrayLikeOrIterator === null) {
  26. throw new TypeError('Array.from requires an array-like object or iterator - not null or undefined');
  27. }
  28. //变长参数,取到参数2和3。并进行类型判断是否是函数。
  29. var mapfn = arguments.length > 1 ? arguments[1] : void undefined,
  30. T; //参数3:thisArg
  31. if (typeof mapfn !== undefined) { //mapfn参数类型检查
  32. if (isCallable(mapfn))
  33. throw new TypeError('Array.from: when provided, the second argument must be a function');
  34. if (arguments.length > 2) {
  35. T = arguments[2];
  36. }
  37. }
  38. //获得类数组的length属性
  39. var len = toLength(item.length);
  40. var arr = isCallable(caller) ? Object(new caller(len)) : new Array(len); //排除(1).from的情况,并且根据len长度创建数组。
  41. let i = 0,
  42. val;
  43. while (i < len) {
  44. val = item[i];
  45. if (mapfn) {
  46. arr[i] = typeof T === 'undefined' ? mapfn(val, i) : mapfn.apply(T, [val, i]);
  47. } else {
  48. arr[i] = val;
  49. }
  50. i++;
  51. }
  52. return arr;
  53. }
  54. })();

Array.prototype.indexOf( )

语义搜索范围内,从头开始匹配,必须是===严格相等,才会返回索引值的位置。
语法Array.prototype.indexOf(searchElement, fromIndex)

/**
*@param    {value} searchElement 需要查找的元素值
*@param {number = 0} fromIndex 查找的开始位置
*@return {number} 查找元素的位置。找不到索引值返回值为-1。
*/

let a = ['a', 'b', 'a', 'd', 'a'];
let b = a.indexOf('a');
let c = a.indexOf('a', 1);
let d = a.indexOf('f');
console.log(b);     //0
console.log(c);     //2
console.log(a);     //["a", "b", "a", "d", "a"]
console.log(d);     //-1

例子获取所有的索引值。

var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
function indexAllOf(arr, element) {
    const indices = [];
    let idx = arr.indexOf(element);
    while (idx != -1) {
        indices.push(idx);
        idx = arr.indexOf(element, idx + 1);
    }
    return indices;
}
console.log(indexAllOf(array, 'a'));

Array.protorype.lastIndexOf( )

语义搜索范围内,从末尾匹配,必须是===严格相等,返回最后一个索引值。
语法Array.protorype.lastIndexOf ( searchElement, fromIndex )

/**
*@param    {value} searchElement 需要查找的元素值
*@param [{number = 0}] fromIndex 查找的开始位置
*@return {number} 查找元素的位置。找不到索引值返回值为-1。
*/
let a = ['a', 'b', 'a', 'd', 'a'];
let b = a.lastIndexOf('a', 4);
let c = a.lastIndexOf('a', 3);
console.log(b);     //4
console.log(c);     //2
console.log(a);     //["a", "b", "a", "d", "a"]

Array.protorype.includes( )

语义搜索数组是否包含某个元素。
关键点该方法可以通过call()、apply()用于类数组对象。
语法Array.protorype.includes( searchElement, fromIndex )

/**
*@param    {value} searchElement 需要查找的元素值
*@param [{number = 0}] fromIndex 查找的开始位置
*@return {Boolean} 
*/
let a = ['a', 'b', 'a', 'd', 'f'];
let b = a.includes('a', 2);
let c = a.includes('a', 3);
console.log(b);     //true
console.log(c);     //false

例子polyfill

Array.prototype.myIncludes = function (searchElement) {
    if (this === null) {
        throw new TypeError('this is null');
    }
    var O = Object(this);
    var len = O.length >>> 0;
    var start = arguments[1] === undefined ? 0 : arguments[1] >> 0;
    var k = Math.max(start >= 0 ? start : len - Math.abs(start), 0);

    while (k < len) {        
        if (O[k] === searchElement) {
            return true;
        }
        k++;
    }
    return false;
}

Array.prototype.find( )

语义根据回调函数,找到第一个满足回调函数的元素值。
关键点该方法可以通过call()、apply()用于类数组对象。
关键点find会遍历稀疏数组的空值,效率比较低,取值为undefined。
关键点即使在遍历过程中,数组元素被删除delete,仍然补值undefined。
关键点遍历过程中增加元素,遍历的长度仍然是初始数组长度。
语法Array.prototype.find(callbackFn(element[, index, array])[, thisArg])

/**
*@param    {function} callbackFn 回调函数返回的是Boolean值。
*@param [this.Arg] 改变回调函数内部的this值。
*@return {value = undefined} 满足回调函数的第一个值。
*/

例子polyfill

Array.prototype.myFind = function (callBackFn) {
    //排除this最为对象时的null值
    if (this === null) {
        throw new TypeError('this is not null');
    }
    var O = Object(this);
    // 满足类数组,检查length属性值非负整数。
    var len = O.length >>> 0;

    //检查参数
    if (typeof callBackFn !== 'function') {
        throw new TypeError('callBackFn is not function');
    }

    // 此方法不行,如果在回调函数中更改数组的长度,那么会导致死循环。
    // for (let key in O) {
    //     if (callBackFn.call(arguments[1], O[key], key, O)) {
    //         return O[key];
    //     }
    // }

    var key = 0;
    while (key < len) {
        if (callBackFn.call(arguments[1], O[key], key, O)) {
            return O[key];
        }
        key++;
    }

    return undefined;
}

Array.prototype.findIndex( )

语义根据回调函数,找到第一个满足回调函数的元素的索引值。
关键点没有找到值时,返回-1。
语法Array.prototype.findIndex(callbackFn(element[, index, array])[, thisArg])

/**
*@param    {function} callbackFn 回调函数返回的是Boolean值。
*@param [this.Arg] 改变回调函数内部的this值。
*@return {value = -1} 满足回调函数的第一个元素索引值。
*/

例子polyfill

Array.prototype.myFindIndex = function (callBackFn) {
    //排除this最为对象时的null值
    if (this === null) {
        throw new TypeError('this is not null');
    }
    var O = Object(this);
    // 满足类数组,检查length属性值非负整数。
    var len = O.length >>> 0;

    //检查参数
    if (typeof callBackFn !== 'function') {
        throw new TypeError('callBackFn is not function');
    }

    // 此方法不行,如果在回调函数中更改数组的长度,那么会导致死循环。
    // for (let key in O) {
    //     if (callBackFn.call(arguments[1], O[key], key, O)) {
    //         return O[key];
    //     }
    // }

    var key = 0;
    while (key < len) {
        if (callBackFn.call(arguments[1], O[key], key, O)) {
            return key;
        }
        key++;
    }

    return -1;
}

Array.prototype.copyWithin(target[, start, end])

语义移动数组元素:将数组内的一部分元素,复制粘贴到数组的另一个位置。
关键点该方法可以通过call()、apply()用于类数组对象。

/**
*@param    {number} target 粘贴数组片段的起点位置
*@param {number = 0} start 复制数组片段开始位置
*@return {number = arr.length} 复制数组片段开始位置(不包含该位置本身)
*/
var a = [1, 2, 3, 4, 5];
var b = a.copyWithin(0, 2);
console.log(b);    //[ 3, 4, 5, 4, 5 ]

例子polyfill

  • 比较两个数字的大小,可以使用Math.max/min 的方法

    //myCopyWithin(target,[start, end])
    //可用于类数组,注意this类型,length属性的类型。
    Array.prototype.myCopyWithin = function (target) {
      if (this === null) {
          throw new TypeError('this is null or not defined')
      }
      //原始值也转为对象。
      var O = Object(this);
    
      //判读类数组中的length属性值,必须是正整数。
      // 1、转化为整数
      var toInteger = function (value) {
          var num = Number(value);
          if (isNaN(num)) { return 0 };
          if (num === 0 || !isFinite(num)) { return num };
          return (num > 0 ? 1 : -1) * Math.floor(Math.abs(num));
      }
      // 2、限制范围,0~finite。除去负数和无限值
      var toLength = function (value) {
          var maxSafeInteger = Number.MAX_SAFE_INTEGER,
              len = toInteger(value);
          return Math.min(Math.max(len, 0), maxSafeInteger);
      }
    
      var len = toLength(this.length);
      var tarNumber = toInteger(target);
      var to =
          tarNumber < 0
              ? Math.max(len + tarNumber, 0)
              : Math.min(tarNumber, len);
    
      var startNumber = toInteger(arguments[1]);
      var start =
          startNumber < 0
              ? Math.max(len + startNumber, 0)
              : Math.min(startNumber, len);
    
      var endNumber =
          arguments[2] === undefined
              ? len
              : toInteger(arguments[2]);
    
      var end =
          endNumber < 0
              ? Math.max(len + endNumber, 0)
              : Math.min(endNumber, len);
    
      //复制数组片段的长度
      var count = Math.min(end - start, len - to);
      var direction = 1;
      if (start < to && to < start + count) {    //后一个判断表示复制粘贴的数组有重叠部分。
          direction = -1;
          start += count - 1;
          to += count - 1;
      }
      while (count > 0) {
          if (start in O) {
              O[to] = O[start];
          } else {
              delete O[to];
          }
          start += direction;
          to += direction;
          count--;
      }
      return O;
    }
    

    Array.prototype.fill(value[, start, end])

    语义在[start, end)区间,填充一样的value值。
    关键点该方法可以通过call()、apply()用于类数组对象。

    /**
    *@param    {number} value 填充的值
    *@param {number = 0} start 复制数组片段开始位置
    *@return {number = arr.length} 复制数组片段开始位置(不包含该位置本身)
    */
    var a = ['a', 'b', 'c', 'd', 'e'];
    console.log(a.fill(1));        //[ 1, 1, 1, 1, 1 ]
    

    例子polyfill

    Array.prototype.myFill = function (value) {
      var relStart = arguments[1],
          relEnd = arguments[2];
    
      //step1: 由于类数组也可以用此方法,判断this、length
      if (this === null) {
          throw new TypeError('this is null');
      }
      var O = Object(this);
      var len = O.length >>> 0;  //len必须是正整数
    
      //step2: 参数类型判断
      relStart >>= 0;
      start =
          relStart < 0
              ? Math.max(len + relStart, 0)
              : Math.min(relStart, len);
    
      relEnd = relEnd === undefined ? len : arguments[2] >> 2;
      end =
          relEnd < 0
              ? Math.max(len + relEnd, 0)
              : Math.min(relEnd, len);
      if (start < end) {
          O[start] = value;
          start++;
      }
      return O;
    }
    

    Array.prototype.flat([depth])

    语义将嵌套的数组结构展开,可以指定递归的层数。

    /**
    *@param    {number=1} depth
    *@return {array} 相同的展开数组。
    */
    

    例子polyfill

  • 尾递归一定要先写递归终止语句。一般是return语句或者是带有副作用的语句。 ```javascript Array.prototype.myFlat = function () { if (!Array.isArray(this)) {

      throw new TypeError('this is not Array');
    

    } var depth = arguments[0] === undefined ? 1 : arguments[0] >>> 0;
    var res = []; var flatDeep = function (arr, depth) {

      arr.forEach(element => {
          if (depth > 0 && Array.isArray(element)) {
              flatDeep(element, depth - 1);
          } else {
              res.push(element);    //递归终止语句
          }
      });        
    

    }; flatDeep(this, depth); return res; }

//运用栈算法,push和pop Array.prototype.myFlat = function () { if (!Array.isArray(this)) { throw new TypeError(‘this is not Array’); } var depth = arguments[0] === undefined ? 1 : arguments[0] >>> 0; var res = [], stack = […this]; //浅拷贝一份原数组

while (stack.length) {
    var popElement = stack.pop();
    if (Array.isArray(popElement)) {
        stack.push(...popElement);           
    } else {
        res.unshift(popElement);
    }
}
return res;

}

<a name="iApZS"></a>
## Array.prototype.flatMap( )
语义数组展开一次,再进行映射。<br />语法flatMap( function callbackFn( currentValue, index, array ) { ... }, [thisArg] )<br />关键点原数组不改变。
```javascript
/**
*@param    {function} callbackFn map函数
*@param [this.Arg] 改变回调函数内部的this值。
*@return {array} 新数组。
*/
var arr = [1, [2], [[3, 4, 5]]];
console.log(arr.flatMap(x => x));
console.log(arr);   //[ 1, 2, [ 3, 4, 5 ] ]

例子polyfill

if (!Array.prototype.flatMap) {
  Object.defineProperty(Array.prototype, 'flatMap', {
    configurable: true,
    writable: true,
    value: function () {
      return Array.prototype.map.apply(this, arguments).flat(1);
    },
  });
}

Array.prototype.map( )

let deepClone = source => {
    let target = Array.isArray(source) ? [] : {};
    for (let key in source) {
        if (source.hasOwnProperty(key)) {
            if (typeof source[key] === 'object' && source[key] !== null) {        //不判断null
                target[key] = deepClone(source[key]);        //递归的返回值target,赋值给target[key]
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

Array.prototype.myMap = function(callbackFn){
    var arg2 = arguments[1] || window,
        len = this.length,
        mapArray = [];
    for (let i = 0; i < len; i++) {
        var element = deepClone(this[i]),
            returnValue = callbackFn.apply(arg2, [element, i , this]);            
            mapArray.push(returnValue);
    }
    return mapArray;
}

Array.prototype.filter( )

let deepClone = source => {
    let target = Array.isArray(source) ? [] : {};
    for (let key in source) {
        if (source.hasOwnProperty(key)) {
            if (typeof source[key] === 'object' && source[key] !== null) {        //不判断null
                target[key] = deepClone(source[key]);        //递归的返回值target,赋值给target[key]
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

Array.prototype.myFilter = function (callbackFn) {

    var backThis = arguments[1] || window;   //保存形参中的this的值,改变回调函数this
    var filterArray = [],
        len = this.length;
    for (let i = 0; i < len; i++) {
        var element = deepClone(this[i]);
        if (callbackFn.apply(backThis, [element, i, this])) {
            filterArray.push(element);
        }
    }
    return filterArray;

}

Array.prototype.every( )

Array.prototype.myEvery = function (callbackFn) {
    var arg2 = arguments[1] || window,
        len = this.length,
        res = true;
    for (let i = 0; i < len; i++) {
        res = callbackFn.apply(arg2, [this[i], i, this]);
        if(res === false){
            break;
        }                                
    }
    return res;
}

Array.prototype.some( )

Array.prototype.mySome = function (callbackFn) {
            var arg2 = arguments[1] || window,
                len = this.length,
                res = false;
            for (let i = 0; i < len; i++) {
                res = callbackFn.apply(arg2, [this[i], i, this]);
                if(res === true){
                    break;
                }                                
            }
            return res;
        }

Array.prototype.reduce( )

Array.prototype.myReduce = function (callbackFn, initial) {
    var len = this.length,
        prev = initial;
    for (let i = 0; i < len; i++) {
        prev = callbackFn(prev, this[i], i, this);
    }
    return prev;
}