数组方法合集

285 阅读6分钟

前言

什么决定了数组中的length

首先我们要探究到底数组的length的值是怎么确定的?

不手动修改数组中的length,访问length

其实length的值是与数组中的","数量最后一个"," 后面到底你有没有填值进去相关 如果最后一个","后面你没有填值进去,length的值就是数组中","的数量 如果最后一个","后面你填值进去(包括你填了null或者undefined),length的值就是数组中","的数量加一

console.log([1,2,3,4,,].length); //结果为5, 因为“,”数量为5,最后一个“,”后面没有填值,length的值为“,”的数量
console.log([1, 2, 3, 4,,null].length);//结果为6, 因为“,”数量为5,最后一个“,”后面填了null,length的值为“,”的数量加一
console.log([1, 2, 3, 4,,undefined].length);//结果为6, 因为“,”数量为5,最后一个“,”后面填了undefined,length的值为“,”的数量加一
console.log([1, 2, 3,,,].length)//结果为5, 因为“,”数量为5,最后一个“,”后面没有填值,length的值为“,”的数量
console.log([1, 2, 3,,,undefined].length)//结果为6, 因为“,”数量为5,最后一个“,”后面填了undefined,length的值为“,”的数量加一

关于empty

我们可以认为undefined和not defined其实不是一个东西,not defined可以认为是一个不合法的值,会被编译解析时更改为undefined或者不更改直接被编译解析时剔除出数组,而undefined或者null都是合法的值,而且empty是数组中特殊的undefined,我们自己赋值的undefined和编译器为我们赋值的undefined在数组中有不同的显示结果

当我们将数组定义为[1, 2, 3, ,,]时,可以认为我们定义后这数组中有三个not defined,但是编译时引擎会对这种情况进行判断处理:

1.判断除最后一个","之外所有的","的前后是不是有not defined,有的话将对应位置赋值为undefined并用empty进行标记显示,但如果单独访问对应的值会显示为undefined

2.对于最后一个","如果前面有not defined,将对应位置赋值为undefined并用empty进行标记显示,如果后面有not defined,则将该位置舍弃

console.log([1, 2, 3,,,])  //[1, 2, 3, empty × 2]
console.log([1, 2, 3,,,][3] === undefined); //true

empty.png

那么如果手动增加undefined或者null会怎样显示?

console.log([1, 2, 3,undefined,undefined]) //[1, 2, 3, undefined, undefined]
console.log([1, 2, 3, null, null]) //[1, 2, 3, null, null]

换句话说自己加undefined和引擎给你加undefined是不一样的

手动改变length

手动改变length可以延长或者缩短数组的长度,增加长度让length值赋值为一个更大的值,可以认为是在编译解析好的原数组里的最后位置再增加若干个 "," 并将 "," 后面的值初始化为undefined

 let  a = [1, 2, 3, 4];
    console.log(a);   //[1, 2, 3, 4]
    a.length = 5;
    console.log(a); //[1, 2, 3, 4, empty]

增加length.png

缩短数组长度可以让length值赋值为一个比原来更小的值,可以认为是将编译解析好的原数组的第x个","开始所有的","(包括了这个x位置) 的后面所有的值都变为not defined作为标记,然后从数组中剔除,这个x的值就由这个length的值来确定,比如原来length为4,将length改为3,则是将第3个","开始所有的","(包括了这个3的位置)后面所有的值都变为not defined作为标记,然后从数组中剔除

let  a = [1, 2, 3, 4];
    console.log(a);   //[1, 2, 3, 4]
    a.length = 3;
    console.log(a); //[1, 2, 3]

减少length.png

数组原型API

toString

Array.prototype.toString方法可以将数组中的各个属性值按照顺序并以","进行连接,通过字符串的形式进行展示,该方法也会对数组进行扁平化处理

console.log(Array.prototype.toString.call([1, 2, 3, 4, 5])); //"1, 2, 3, 4, 5"
console.log([1,[2,[[3,[4]],5]],null, [6,[7],8],,].toString()); //"1,2,3,4,5,,6,7,8,"

toString的实现与length的值相关,我们可以自己手写一个array_toString方法并将其绑定在Array.prototype上,该方法用数组调用的执行效果和数组原型上的toString一致:

function array_toString() {


      let str = '';

      if(this.length === 0){
        return '';
      }

      for(let i = 0; i < this.length; i++) {
        if(i !== this.length - 1){
          if(Object.prototype.toString.call(this[i]) === "[object Array]"){
            str += this[i].array_toString() + ',';
          }else{
            if(this[i] != undefined){
              str += this[i] + ',';
            }else{
              str += ','
            }
          }
        }else{
          if(Object.prototype.toString.call(this[i]) === "[object Array]"){
            str += this[i].array_toString();
          }else{
            if(this[i] != undefined){
              str += this[i];
            }
          }
        }
      }
      return str;
    }

    Array.prototype.array_toString = array_toString;
    console.log([1,[2,[[3,[4]],5]],null, [6,[7],8],,].array_toString());  //"1,2,3,4,5,,6,7,8,"

forEach方法

手写一个forEach方法

function my_forEach(fn){
    let _this = [];
    for(let i = 0; i < this.length; i++){
      _this[i] = fn(this[i], i);
    }
    return _this;
  }

  Array.prototype.my_forEach = my_forEach;

  let a = [1, 2, 3];

  let b = a.my_forEach((item, idx) => {
    console.log(item, idx);
    return item + 1;
  });

  console.log(b);

push方法

push的返回值是修改后的原数组的长度 手写一个自己的push方法

function my_push() {
    let this_length = this.length;
    for (let i = 0; i < arguments.length; i++) {
      this[this_length + i] = arguments[i];
    }
    return this;
  }

  Array.prototype.my_push = my_push;

  let a = [1, 2];
  let b = a.my_push(3, 4, 5);
  console.log(b);
  console.log(a === b);

pop方法

pop方法返回值是删除的值 手写pop方法

function my_pop(){
    if(this.length > 0){
      this.length -= 1;
    }
    return this;
  }

  Array.prototype.my_pop = my_pop;

reverse方法

reverse方法返回的是原数组,也就是说操作的是原数组 手写reverse方法

  function my_reverse(){
    let reverse_arr = [];
    if(!this.length){
      return reverse_arr;
    }
    let last_idx = this.length - 1;
    for(let i = this.length - 1; i >= 0; i--){
      reverse_arr[last_idx - i] = this[i];
    }
    
    for(let i = 0; i < this.length; i++){
      this[i] = reverse_arr[i];
    }

    return this;
    
  }

  Array.prototype.my_reverse = my_reverse;

unshift方法

unshift和push一样返回值是修改后的数组的长度 手写unshift方法

 function my_unshift(){
    let new_arr = [];
        new_arr.length = this.length + arguments.length;
    let new_arr_len = new_arr.length,
        arg_len = arguments.length;

    for(let i = 0; i < new_arr_len; i++){
      if(i < arg_len){
        new_arr[i] = arguments[i];
      }else{
        new_arr[i] = this[i - arg_len];
      }
    }

    for(let i = 0; i < new_arr_len; i++){
      this[i] = new_arr[i];
    }

    return this;
  }

  Array.prototype.my_unshift = my_unshift;

shift方法

shift方法和pop一样,返回值是删除的值 手写shift方法

function my_shift(){
    if(!this.length){
      return this;
    }
    let rev_arr = this.my_reverse(),
        re_rev_arr = rev_arr.my_pop().my_reverse(),
        re_rev_arr_length = re_rev_arr.length;
        
    for(let i = 0; i < re_rev_arr_length; i++){
      this[i] = re_rev_arr[i];
    }

    
    this.length -= 1;
    return this;
    
  }

  Array.prototype.my_shift = my_shift;

sort方法

function my_sort (fn) {
    for(let i = 0; i < this.length; i++){
      for(let j = i;j < this.length; j++){
        if(fn(this[i], this[j]) >= 0){
          temp = this[i];
          this[i] = this[j];
          this[j] = temp;
          // console.log(this[i]);
        }
      }
    }

    return this;
  }
  Array.prototype.my_sort = my_sort;

乱序方法

  function compate(){
    return Math.random() - 0.5;
  }

  console.log(a.my_sort(compate));

concat方法

concat方法中如果传入一维数组,可以认为是将数组的中的元素作为若干个参数进行传递,多维数组的话,只会去掉外面那一层,而且concat返回值是一个新的数组

  let a = [1, 2, 3];
  let b = a.concat(6,[4,5]);
  console.log(a, b);  //[1, 2, 3]  [1, 2, 3, 6, 4, 5]
  
  
  let c = [1, 2, 3];
  let d = c.concat(6, [[4], 5]);
  console.log(c, d); // [1, 2, 3]  [1, 2, 3, 6, [4], 5]

slice方法

slice方法返回值是一个新数组,第一个参数是截取的开始位置,第二个参数是截取的结束位置(左闭右开区间)

    let a = [1, 2, 3, 4, 5];
    console.log(a.slice(2));  //[3, 4, 5]
    console.log(a.slice(2, 4)); //[3, 4]
    console.log(a.slice(4, 2)); //[]
    console.log(a.slice(-2)); //[4, 5]
    console.log(a.slice(-2, -1)); //[4]

    let str = '123456';
    console.log([].slice.call(str)); //["1", "2", "3", "4", "5", "6"]

    function test() {
      console.log([].slice.call(arguments)); //[1, 2, 3, 4, 5]
    }

    test(1, 2, 3, 4, 5);

splice方法

splice方法返回的是更改原数组从而生成的新数组,也就是说这个方法会更改原数组,所谓的更改就不只是删除,也包括增加 splice方法如果只有第一个参数,那截取的长度由第一个参数指定的位置决定,指定位置之后的参数全部从原数组截取掉然后进入新数组 splice方法如果没有传参或者第二个参数非法,都会返回空数组 如果传入的第一个参数是非法的,则会返回一个与原数组内容一样的新数组,同时原数组变为空数组

indexOf/lastIndexOf

Array.of 填充单个的值 Array.from 来源的是类数组 将类数组转换为真正的数组 Array.keys Array.values Array.entries Array.includes Array.copyWithin arr.fill(es6) 数组遍历的方法:forEach/filter/some/every/map/reduce/reduceRight find:参数为函数,返回值为第一个符合条件的元素 findIndex:参数为函数,返回值为第一个符合条件的元素的索引 forEach对于空数组,传入的函数并不会执行,稀疏数组中的empty项也不会导致函数的执行 这和for循环不一样 map映射关系,有返回值

    let a = ['a', 'b', 'c'];
    let b = [0, 1].map(function (item, idx, arr) {
      return this[item];
    }, a);
    console.log(b);

some/every some/every返回值为布尔值