xiaochengzi6 / Blog

个人博客
GNU Lesser General Public License v2.1
0 stars 0 forks source link

数组方法的实现 #50

Open xiaochengzi6 opened 2 years ago

xiaochengzi6 commented 2 years ago

title: "数组中的常规操作"


数组与类数组的转换

ES5 环境下

  1. Array.prototype.slice.call(obj)
var obj = {
  0: 0,
  1: 1,
  2: 2,
  length: 3
}

var arr = Array.prototype.slice.call(obj)
console.log(arr) // [0,1,2]
  1. [].slice.call(obj)
[].slice.call(arr) // [0,1,2]

ES6环境下

  1. Array.from()
let arr = Array.from(obj)
console.log(arr) // [0,1,2]
  1. [...obj] 首先被转换的对象有 iterable 接口
var obj = {
  0: 0,
  1: 1,
  2: 2,
  length: 3,
    // 迭代器
  [symbol.iterable]: () => {}
}
  1. 使用能返回数组的迭代方法 比如 map、filter 。这些也要基于对象有迭代入口 Iterable
Array.map.call(obj,(x)=>{x})

4.使用 Array.concat()

function sum(a, b) {
  let args = Array.prototype.concat.apply([], arguments);//apply方法会把第二个参数展开
  console.log(args.reduce((sum, cur) => sum + cur))
}
sum(1, 2);//3

concat 默认情况下不会将类数组对象视作数组——仅在 Symbol.isConcatSpreadable 被设置为真值(例如,true)时才会将类数组对象视作数组。具体参考MDN

concat 方法创建一个新的数组,它由被调用的对象中的元素组成,每个参数的顺序依次是该参数的元素(如果参数是数组)或参数本身(如果参数不是数组)。它不会递归到嵌套数组参数中。

函数中的 arguments 可以使用 concat 的方式 去将类数组参数展开这样说明是具有 Symbol.isConcatSpreadable = true

合并数组

ES5环境

1.forEach

var arr_1 = [1,2,3];
var arr_2 = [4,5,6];
// arr_1 + arr_2
var arr_3 = arr_2.map(function(value,index,arrs){
    arr_1.push(...value);
})

ES6环境

  1. Array.concat()
var arr = [0,1,2];
var arr_1 = arr.concat(3,4)
// [ 0, 1, 2, 3, 4 ]

分割数组

  1. slice
  2. splice

ES5中的数组方法

  1. forEach()
  2. map()
  3. indexof()
  4. lastIndexof()
  5. reduce()
  6. reduceRight()
  7. push()
  8. shift()
  9. pop()

ES6中的数组方法

(一)判断是否是数组

Array.isArray()

if(!Array.prototype.isArray){
    Array.prototype.isArray = function (obj) {
        if(
             o &&
          typeof o === 'object' &&
          o.length &&
          (o.length !== Infinity || o.length !== -Infinity) &&
          o.length >= 0 &&
          o.length < Math.pow(2,53) - 1
        ){
            return true
        }
        else{
            return false
        }
    }
}

(二)转换

toLocaString()

valueOf()

toString()

(三)遍历

  1. every()

    every(callback, thisArr)

    1.1 若收到一个空数组,此方法在一切情况下都会返回 true

    1.2 不会改变数组,数组在遍历前就已经确定。如果更改某值,callback会取访问到那一刻的值

    1.3 不会为那些被删除或从未被赋值的索引调用

    作用:当所有的元素都符合条件才会返回true

    if (!Array.prototype.myevery) {
     Array.prototype.myevery = function (callback, thisArr) {
       var T, K;
       if (this == null) {
         throw TypeError("this is no undefiend");
       }
       if (typeof callback !== "function") {
         throw TypeError(`${callback} is no function`);
       }
       if(arguments.length > 1){
         T = thisArr;
       }
       var O = Object(this);
       //
       var len = O.length >>> 0;
       K = 0;
       while (K < len) {
         var kvalue, carrbackValue;
         if(K in O) {
           kvalue = O[K]
           carrbackValue = callback.call(T,kvalue,K,O);
         }
         if(!carrbackValue){
           return false;
         }
         K++;
       }
       return true;
     };
    }
    
    var arr = [1, 2, 3, 4];
    var a = arr.myevery(function (target, index, arrs) {
     // console.log(`${target} + ${index} + ${arrs}`);
     if(target > 0) {
       return true;
     }
    });
    console.log(a)
    
  2. some()

    some(callback, thisArr);

    作用:数组中有至少一个元素通过回调函数的测试就会返回true;所有元素都没有通过回调函数的测试返回值才会为false。

    2.1 some()为数组中的每一个元素执行一次 callback函数,直到找到一个使得 callback 返回一个“真值”(即可转换为布尔值 true 的值)。如果找到了这样一个值,some() 将会立即返回 `true

    2.2 callback 只会在那些”有值“的索引上被调用,不会在那些被删除或从来未被赋值的索引上调用。

    2.3 如果一个thisArg参数提供给some(),它将被用作调用的 callbackthis 值。否则, 它的 this value将是 undefinedthis的值最终通过callback来观察.

    if (!Array.prototype.mysome) {
     Array.prototype.mysome = function (callback, thisArr) {
       var T, K;
       if(this == null ){
         throw TypeError('this is undefiend')
       }
       if(typeof callback !== 'function'){
         throw TypeError(`${callback} is no Function`);
       }
    
       // if(arguments.length > 1) {
       //   T = thisArr;
       // }
       thisArr = arguments.length >= 2 ? arguments[1] : void 0;
    
       var O = Object(this);
       var len = O.length >>> 0;
    
       for( K = 0 ; K < len; K++){
         if(K in O && callback.call(T, O[K], K, O)){
           return true;
         }
       }
       return false;
     }
    }
    
    var arr = [1, 2, 3, 4];
    var a = arr.mysome(function (target, index, arrs) {
     // console.log(`${target} + ${index} + ${arrs}`);
     if(target > 0) {
       return true;
     }
    });
    console.log(a)
  3. map()

    map(callback, thisArr) map会生成一个新数组

    1. 每一个元素都会调用 callback 函数。
    2. 每次执行后的返回值保存在数组中返回
    3. callback 函数只会在有值的索引上调用,那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
    4. 如果 thisArg 参数提供给map,则会被用作回调函数的this值。否则[undefined](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined)会被用作回调函数的this
if (!Array.prototype.myMap) {
  Array.prototype.myMap = function (callback, thisArr) {
    var This, K, A;
    if (typeof this == null) {
      throw TypeError("this is undefiend");
    }
    if (typeof callback !== "function") {
      throw TypeError(`${callback} is no Function `);
    }
    if(arguments.length) {
      This = thisArr;
    }
    var O = Object(this);
      // 很特殊的写法 提高了程序的鲁棒性
    var len = O.length >>> 0;
    K = 0;
    A = new Array(len);

    while (K < len) {
      var keyValue, callbackValue;
      if (K in O) {
        keyValue = O[K];
        callbackValue = callback.call(This, keyValue, K, O);
        // A.push(callbackValue)
        A[K] = callbackValue;
      }
      K++;
    }
    return A;
  };
}

var arr = [1, 2, 3, 4];
arr.myMap(function (target, index, arrs) {
  console.log(`${target} + ${index} + ${arrs}`);
});

map(callback,thisValue) callback函数中最常用的就是当前遍历的元素,但是这并不意味这callback函数只接受一个参数。

var xs = ['10', '10', '10'];
xs = xs.map(parseInt); // [10, NaN, 2]
// 正确的做法
xs = xs.map( target => {parseInt(target)} )
  1. fliter()

    作用:创建一个新数组, 其包含通过所提供函数测试的所有元素。

    参数:fliter(callback, thisValue)

    4.1 filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或[等价于 true 的值](https://link.juejin.cn/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FGlossary%2FTruthy)的元素创建一个新数组。

    4.2 filter 遍历的元素范围在第一次调用 callback 之前就已经确定了。在调用 filter 之后被添加到数组中的元素不会被 filter 遍历到

    4.3 如果已经存在的元素被改变了,则他们传入 callback 的值是 filter 遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。

    4.4 如果为 filter 提供一个 thisArg 参数,则它会被作为 callback 被调用时的 this 值。否则,callbackthis 值在非严格模式下将是全局对象,严格模式下为 undefined

    if (!Array.prototype.myfilter) {
     Array.prototype.myfilter = function (callback, thisArr) {
       if (this == null) {
         throw TypeError("this is undefiend");
       }
       if (typeof callback !== "function") {
         throw TypeError(`${callback} is no Function`);
       }
       var T = arguments.length > 2 ? thisArr : void 0;
       var i = -1,
         O = Object(this),
         len = O.length,
         arr = new Array(len),
         c = 0;
       if (!T) {
         while (++i != len) {
           if (i in O) {
             if (callback(O[i], i, O)) {
               arr[c++] = O[i];
             }
           }
         }
       } else {
         while (++i != len) {
           if (i in O) {
             if (callback.call(T, O[i], i, O)) {
               arr[c++] = O[i];
             }
           }
         }
       }
       arr.length = c
       return arr;
     };
    }
    
    var arr = [1, 2, 3, 4];
    var a = arr.myfilter(function (target, index, arrs) {
     // console.log(`${target} + ${index} + ${arrs}`);
     if (target > 2) {
       return true;
     }
    });
    console.log(a);
  2. forEach()

forEach(callback, thisArr) 的特点:

  1. 对数组每个方法都执行callback,但都返回undefiend;
  2. 该方法按升序为数组中含有效值的每一项执行一次 callback 函数,未初始化的项将被跳过(例如在稀疏数组上)。
  3. 如果已经存在的值被改变,则传递给 callback 的值是 forEach() 遍历到他们那一刻的值。
  4. 已删除的项不会被遍历到.
  5. [若删除已遍历的数组元素可能会使后续遍历中跳过某个元素](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#%E5%A6%82%E6%9E%9C%E6%95%B0%E7%BB%84%E5%9C%A8%E8%BF%AD%E4%BB%A3%E6%97%B6%E8%A2%AB%E4%BF%AE%E6%94%B9%E4%BA%86%EF%BC%8C%E5%88%99%E5%85%B6%E4%BB%96%E5%85%83%E7%B4%A0%E4%BC%9A%E8%A2%AB%E8%B7%B3%E8%BF%87%E3%80%82)
  6. 如果 thisArg 参数有值,则每次 callback 函数被调用时,this 都会指向 thisArg 参数。如果省略了 thisArg 参数,或者其值为 nullundefinedthis 则指向全局对象。按照函数观察到 this 的常用规则callback 函数最终可观察到 this 值。
if(!Array.prototype.myforEach){
  Array.prototype.myforEach = function (callback, thisArr) {
    var T, K;
    if(this){
     throw new TypeError(`this is undefiend`);
    }
    if(typeof callback !== "function" ){
      throw new TypeError(`${callback} is no Function`);
    }
    var o = Object(this);
    //1.所有非数值转换成0
    //2.所有大于等于 0 等数取整数部分。
    var len = o.length >>> 0

    if(arguments.length > 0) {
      T = thisArr;
    }

    K = 0;
    while( K < len){
      var keyValue;

      if(K in o) {
        keyValue = o[K];
      }
      callback.call(T, keyValue, K, o);
      K++;
    }
    // return undefiend;
  }
}
//测试
var arr = [1,2,3,4];
arr.myforEach(function(target,index,arrs){
  console.log(`${target} + ${index} + ${arrs}`)
},this)

4.排序

reveter()

sort()

(五)操作

1.concat()

2.slice()

返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

2.1 参数slice([begin[, end]])

begin:提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。

end: 提取终止处的索引(从 0 开始),在该索引处结束提取原数组元素。slice 会提取原数组中索引从 beginend 的所有元素

if(!Array.prototype.mySlice) {
  Array.prototype.mySlice = function (begin, end) {
    end = typeof end !== 'undefined' ? end : this.length;

    var i, clone = [], size, lenght = this.length;
    // bagin
    var start = begin || 0;
    start = start >= 0 ? start : Math.max(0,len - start);

    // end
    var upto = typeof end === 'number'?  Math.min(end, len) : len;
    if(end < 0) { upto = len + end};

    size = end - start;

    if(size) {
      if(this.charAt) {
        for( i = begin; i < size; i++){
          clone[i] = this.charAt(start + i)
        }
      }
      else{
        for(i = 0; i < size; i++){
          clone[i] = this[start + 1]
        }
      }
    }
    return clone;
  }
}

3.splice()

作用:通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

(六)查找

1.严格查找

1.1indexof()

1.1.1 参数:indexof(searElement,fromIndex) searElement:目标元素 ,fromIndex:开始查找的位置;

1.1.2 作用:indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

if (!Array.prototype.myIndexof) {
  Array.prototype.myIndexof = function (searchElement, fromIndex) {
    if (this == null) {
      throw new TypeError("");
    }
    var O = Object(this);
    var len = O.length;
    if(len == 0){
      return -1
    }

    var K = +fromIndex || 0;
    if (Math.abs(K) > Infinity) {
      K = 0;
    }
    if(K > len) {
      return -1;
    }

    var I = Math.max(K >= 0 ? K : len - Math.abs(K), 0)
    while (I < len){
      if(I in O && O[I] === searchElement){
        return I
      }
      I++
    }
    return -1
  };
}

var arr = [1, 2, 3, 4, 5];
var a = arr.myIndexof(1,0);
console.log(a);

1.2lastIndexOf()

作用:返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1。

1.2.1 参数: lastIndexOf(searchElement, fromIndex);

searchElement: 被查找的元素。

fromIndex: 从此位置开始逆向查找。默认为数组的长度减 1(arr.length - 1),即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。

if (!Array.prototype.mylaseIndexof) {
  Array.prototype.mylaseIndexof = function (searchElement, fromIndex) {
    var K;
    if(this == null ) {
      throw new TypeError('')
    }
    var O = Object(this);
    var len = O.length >>> 0;
    if(len == 0 ){
      return -1
    }
    var n = +fromIndex || 0;
    if(Math.abs(n) == Infinity || Math.abs(n) > len-1 ) {
      return -1
    }

    K = n >= 0 ? n : len - Math.abs(n)
    while(K <= len ){
      var i = len - K;
      if(O[i] === searchElement){
        return i
      }
      K++
    }
    return -1
  };
}

var arr = [1, 2, 3, 4, 5];
var a = arr.mylaseIndexof(1,3);
console.log(a);

2.断言查找

2.2find()

2.2.1 参数:find(callback,thisArr) 回调函数:callback(element, index, array) thisArr: 用作回调时 this 对象

2.2.2 callback函数会为数组中的每个索引调用即从 0length - 1,而不仅仅是那些被赋值的索引。(哪怕时稀疏数组都会被调用这个函数)

2.2.3 当找到了这样一个元素后,该方法会立即返回这个元素的值,否则返回 undefined

作用:数组中第一个满足所提供测试函数的元素的值,否则返回 [undefined](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined)

if (!Array.prototype.myfind) {
  Array.prototype.myfind = function (callback) {
    var T, K;
    if(this == null){
      throw new TypeError('')
    }
    if(typeof callback !== 'function'){
      throw new TypeError('')
    }
    T = arguments.length >= 2 ? arguments[1] : void 0;
    var O = Object(this);
    var len = O.length;
    K = 0
    while(K < len) {
       if(callback.call(T, O[K], K, O)){
         return O[K]
       }
      K++
    }
    return void 0;
  };

}

var arr = [1,2,3,4,5]
var a = arr.myfind(function (target, index, arrs) {

  if(target > 2){return true}
  return false
});
console.log(a);

2.3findIndeof()

这个和上面差不多就是在返回的时候返回数组的索引值

(七)归并

  1. reduce()

    作用:reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

    1.1 参数:reduce(callback,initiValue) callback 接收四个参数:1.accumulator-累加器 2. currentValue-当前值 3. currentIndex-当前索引 4. array-原数组

    1.2 回调函数第一次执行时,accumulatorcurrentValue的取值有两种情况:如果调用reduce()时提供了initialValueaccumulator取值为initialValuecurrentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。

    1.3 如果数组为空且没有提供initialValue,会抛出[TypeError] 。如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。

    if (!Array.prototype.myReduce) {
     Array.prototype.myReduce = function (callback, initValue) {
       if(this == null  ) {
         throw new TypeError('this is undefiend')
       }
       if(typeof callback !== 'function') {
         throw new TypeError(`${callback} is no Function`)
       }
       var O = Object(this);
       var len = O.length >>> 0;
       var value, K = 0;
       // initValue = 0 && this中是空值
       if(arguments.length >= 2) {
         value = arguments[1];
       }else{
           // 很巧妙的去求数组中的第一个值
         while(K < len && !(K in O)){
           K++
         }
         // 数组空值报错
         if(K > len) {
           throw new TypeError('数组是空的');
         }
         value = O[K++]// 这里是K
       }
        // 如果 k = len 会直接返回
       while(K < len){
         if(K in O) {
           value = callback(value, O[K], K, O);
         }
         K++
       }
    
       return value;
     };
    }
    // test
    var a = arr.myReduce(function (initvalue,target, index, arrs) {
     if(initvalue.indexOf(target) === -1){
       initvalue.push(target) ;
     }
     return initvalue;
    },[]);
    console.log(a);
  2. reduceRight()

    用法和 reduce 类似 但是是从数组的最后一个元素开始遍历

    if (!Array.prototype.myReduceRight) {
     Array.prototype.myReduceRight = function (callback) {
       if(this == null) 
       throw new TypeError('')
    
       if(typeof callback !== 'function'){
         throw new TypeError('')
       }
       var O = Object(this);
       var len = O.length;
       var value, K = len;
       if(arguments.length >= 2 ) {
         value = arguments[1]
       }else{
         while(K < len && !(K in O)){
           K--
         }
         if(K > len) {
           throw new TypeError('')
         }
         value = O[K--]
       }
    
       while(K > -1) {
         if( K in O){
           value = callback(value, O[K], K, O);
         }
         K--
       }
       return value
     };
    
    }
    
    var arr = [1,2,3,4,5]
    var a = arr.myReduceRight(function (initvalue,target, index, arrs) {
     if(initvalue.indexOf(target) === -1){
       initvalue.push(target) ;
     }
     return initvalue;
    },[]);
    console.log(a);
    

(八)迭代器方法

1.keys()

2.values()

3.entries()

(九)复制和填充

1.fill()

作用: 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。

fill(value[, start[, end]])

value用来填充数组元素的值。

start 可选 起始索引,默认值为0。

end 可选 终止索引,默认值为 this.length

2.copyWithin()

(十)创建数组

1.Array.from()

一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

1.1 参数:Array.from(obj, mapFn, thisArg) === Array.from(obj).map(mapFn, thisArg)

arrayLike: 伪数组或者可迭代对象

map:每个元素都会执行该回调函数

thisArg: 执行回调函数 mapFnthis 对象。

if(!Array.myFrom){
  Array.prototype.myFrom = (function(){
    var isNum = function (num) {
      if(num && isFinite(num)&& num >= 0 && num < Math.pow(2,53)-1 ){
          return true
        }
        else{
          return false
        }
    }
    var toFunction = function (func) {
      return typeof func === 'function' || Object.prototype.toString(func) === '[object Function]';
    }

    return function from(arrayLike/*, callback, thisArg*/) {
      if(arrayLike == null){
        throw new TypeError('')
      }
      var mapFn = arguments.length > 1 ? arguments[1] : void 0;
      var T;
      if(typeof mapFn !== 'undefined'){

        if(toFunction(mapFn)){
          throw new TypeError('')
        }
        if(arguments.length > 2) {
          T = arguments[2]
        }
      }

      var C = this;
      var O = Object(arrayLike);
      var len = isNum(O.length) ? O.length : 0;

      var A = toFunction(C) ? Object(new C(len)) : new Array(len);
      var K = 0, 
      kvalue;

      while( K < len ) {
        kvalue = O[K]
        if(mapFn) {
          A[K] = typeof T === 'undefined' ? mapFn(kvalue, K) : mapFn.call(T, kvalue, K);
        }else{
          A[K] = kvalue;
        }
        K++;
      }
      return A
    }
  }())
}
var a = Array.prototype.myFrom({
  0: 0,
  1: 1,
  2: 2, 
  3: 3,
  length: 4
})
console.log(a)

2.Array.of()

作用:方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

参数: 任意参数

创建的数组为不含 undefined 数组

if(!Array.myof){
  Array.prototype.myof = function(element){
    var len = arguments.length;
    if(len == 0){
      return [];
    }
    var K = 0;
    var I = 0;
    var A = new Array();
    while(K < len){
      if(arguments[K]){
        A[I++] = arguments[K]
      }
      K++;
    }
    return A;

  }
}
var a = Array.prototype.myof(1,2,3,4,5,6)
console.log(a)

(十一)栈方法、队列方法

1.unshift()

2.shift()

3.push()

4.pop()

参考

[【建议收藏】徒手实现24+数组方法,谁说你只是“会用”数组?---代码是参考 mdn 的](https://juejin.cn/post/7012765060737007624)