美文网首页让前端飞Vue.js专区
从头开始复习js之让你彻彻底底搞清楚数组

从头开始复习js之让你彻彻底底搞清楚数组

作者: KlivitamJ | 来源:发表于2019-04-20 19:13 被阅读20次

    关于数组这一块,从开始写项目开始就一直在用,但是基本都没有整理过,怎么说呢?既然在复习这个东西,那我今天正好在复习这个数组,就集中整理一下在实际开发中用到频率最高的数据吧。

    一、 es5中的数组方法

    es5中定义了22个数组的方法,这22个方法的使用频率基本贯穿整个前端开发,如果你想进入前端,这22个应该成为你的本能反应。下面就针对这22个,我来简单做一下代码的实例吧:

    1.1、 数组定义

    关于数组定义一般有两种方式:

    • new字符
    • 数组字面量
    • 数组的清空

    具体的定义如下:

    var testArray = new Array();
    var testArray1 = [1,2,3,4,5];
    

    额,这里重点提一下数组的清空,我建议使用:

    var testArray = [];
    

    1.2、 数组检测

    在es5里面检测数据类型是不是数组的方式主要有两个,length主要用来获取数组的长度:

    • instanceof:判断构造函数
    • isArray
    • length:返回数据内元素的个数

    具体的代码如下:

    var array = [1,2,3,4,5];
    var object = { key:1,value:"hello"};
    
    console.log(array instanceof Array); // true
    console.log(object instanceof Array); // false
    
    console.log(Array.isArray(array)); // true
    console.log(Array.isArray(object)); // false
    
    console.log(array.length) // 5
    

    1.3、 数组转化成字符串

    数据转化成字符串一般有三种方式:

    • toLocaleString
    • toString
    • join

    来看下面一段代码:

    console.log(array.toString()); // 1,2,3,4,5
    console.log(array.toLocaleString()); // 1,2,3,4,5
    console.log(array.join("")); // 12345
    console.log(array.join("&&")); // 1&&2&&3&&4&&5
    console.log(array.join()); // 1,2,3,4,5
    

    首先toStringtoLocaleString是将数组直接转化成String,中间用逗号分隔。(到现在为止我都不知道两者在Array中有什么区别,我自己做实验只在Date中发现了一点异常。)

    其次是join:数组元素先被转换为字符串,再将这些字符串用 separator 分割连接在一起。如果没提供分隔符,将一个逗号用作分隔符。

    1.4、 数组的添加和删除

    数组的增删操作的属性一共下面几种:

    • push:在数组的最末尾添加元素
    • pop:在数据的最末尾删除元素
    • unshift: 在数组的最前面添加一个元素
    • shift: 删除数组的最前面一项

    来看下面一段代码:

    var array = [1,2,3,4,5];
    array.push(999); // [ 1, 2, 3, 4, 5, 999 ]
    array.pop(); // [ 1, 2, 3, 4, 5 ]
    array.unshift(999); // [ 999, 1, 2, 3, 4, 5 ]
    array.shift(); // [ 1, 2, 3, 4, 5 ]
    

    1.5、 数组的排序

    关于数组的排序,主要有两个方法:

    • reverse 翻转数组
    var array = [1,2,3,4,5];
    array.reverse(); // [ 5, 4, 3, 2, 1 ]
    
    • sort //数组中按元素来排序,默认是按照首个字符的Unicode编码排序,所以默认是从小到大排序。

    sort方法接收两个参数,第一个是数组,第二个参数如果存在,就会按照第二个函数的返回值来排序,如果不存在 缺省值是从小到大。来看下面一段代码:

    array.sort() // [ 1, 2, 3, 4, 5 ]
    
    array.sort((a,b)=>{
        return b-a;
    });
    console.log(array); // [ 5, 4, 3, 2, 1 ]
    

    关于数组的排序这一块的源码呢,之前我有看过一部分,我发现,在源码中数组长度小于23的时候采用的是插入排序,反之则用的是快速排序。(甚至命名都是InsertionSort,QuickSort),关于排序的实现方式,可以去看一下 我之前写的排序的文章来复习一下,我觉得我自己写的还比较好理解的。

    1.6、 数组元素的裁剪/缝补

    • concat: 合并数组
    var array1 = [1,2,3];
    var array2 = ["1","2","3"];
    console.log(array1.concat(array2)); // [1,2,3,"1","2","3"];
    
    • slice: 数组截取
    var arr = [1, 2, 3, "a", "b", "c"];
    console.log(arr.slice(3));      //["a", "b", "c"]
    console.log(arr.slice(0,3));      // [1, 2, 3]
    console.log(arr.slice(0,10));      // [1, 2, 3, "a", "b", "c"]
    console.log(arr.slice(7,10));      //[]
    console.log(arr.slice(-2));      // ["b", "c"]
    console.log(arr.slice(3,0));      // []
    console.log(arr); // 
    

    这里值得我们注意的点是:
    1、 slice默认接收两个参数,slice(start,end),表示从开始截取的位置和截取结束的位置。
    2、 如果只填写一个参数,是从该位置开始向后截取,直到末尾。
    3、 如果只填写一个负数参数的话,是从末尾开始向前截取。
    4、 如果第一个参数比后面大,返回值为空数组。
    5、 如果截取的开始位置大于数组长度返回空数组。
    6、 如果截取的结束位置大于数组的长度,返回开始截取到数据的末尾的数列。

    • splice:数组的删除,删除,增加的数据

    首先来看一个基础的例子

    var arr = [1, 2, 3, "a", "b", "c"];
    arr.splice(2);// [ 1, 2 ]
    arr.splice(0,1); // [2]
    

    然后我们再来看一个带插入的例子:

    var arr1 = [1, 2, 3, "a", "b", "c"];
    arr1.splice(3,2,"hello","world");// [ 1, 2, 3, 'hello', 'world', 'c' ]
    

    值得我们注意的点是:
    1、 splice的参数含义是:splice(开始操作的索引值,删除几项,后面的n项代表从开始操作的索引值开始插入的项)
    2、 如果只传入一个参数,默认返回0到指定参数的值

    1.7、 数组的查找

    • indexof/lastIndexOf,从数组的前/后查数据的索引
      来看下面一个例子
    var arr = [1, 2, 3, "a", "b", "c"];
    console.log(arr.indexOf("b")); // 4
    console.log(arr.lastIndexOf("b")); // 4
    console.log(arr.indexOf("es")); // -1
    

    这里值得注意的是:indexOf是去查询元素第一次出现的索引,而lastIndexOf是去查询元素最后一次出现的索引。

    • every 如果数组内的每一个元素都满足一个要求,就返回true
    var arr = [ 1, 2, 3, 4, 5 ];
    
    arr.every((item,index,array)=>{
        if(item>0) return true;
        return false;
    }); //true
    
    arr.every((item,index,array)=>{
        if(item>3) return true;
        return false;
    }); // false
    

    从上面的代码,我们可以看出:
    1、 every接收一个函数,函数的返回参数有三个,遍历的项,当前遍历的index和数组本身
    2、 当函数的全部项都返回true的时候every才成立;有一项不返回true,every将不会成立。

    • some:如果数组内存在一个元素满足回调函数内的要求,就返回true
      关于some可以跟every做对比记忆。
    var arr = [ 1, 2, 3, 4, 5 ];
    
    arr.some((item,index,array)=>{
        if(item>4) return true;
        return false;
    }); //true
    
    arr.some((item,index,array)=>{
        if(item>7) return true;
        return false;
    }); // false
    

    从上面的代码,我们可以看出
    1、 some接收一个函数,函数的返回参数有三个,遍历的项,当前遍历的index和数组本身
    2、 当函数的全部项都返回false的时候some就会返回false;有一项返回true,some都将成立。

    • filter:返回数组内满足回调函数的元素
      同样这个函数也能对比和上面两个记忆。来看下面一段代码。
    var arr = [ 1, 2, 3, 4, 5 ];
    
    arr.filter((item,index,array)=>{
        return item>3;
    }); //[ 4, 5 ]
    

    从上面可以看出来,filter将会筛选出满足条件的数列出来。

    • forEach: 数组遍历
      这个就不做演示了,主要是遍历,跟for一样

    1.8、 数组的重装和合并

    • map:对于数组的每一项都用函数进行重组,如果不return,返回undefined
      关于map方法,其实在开发中用到的真的特别多,来看下面一个例子:
    var arr = [ 1, 2, 3, 4, 5 ];
    
    arr.map((item,index,array)=>{
        if(item>3){
            return {key:"小于三的数",value:item}
        }
        return {key:"大于二的数",value:item}
    }); 
    //[ { key: '大于二的数', value: 1 },
    // { key: '大于二的数', value: 2 },
    // { key: '大于二的数', value: 3 },
    // { key: '小于三的数', value: 4 },
    // { key: '小于三的数', value: 5 } ]
    

    从上面我们可以得出,map方法是将每一项的返回值重新组装成一个新的数组,如果没有return会返回。

    • reduce/reduceRight:从左/右开始对元素的累加。
    var arr = [ 1, 2, 3, 4, 5 ];
    
    var result1 = arr.reduce((sum,item)=>{
        return sum*10 + item;
    });
    
    var  result2 = arr.reduceRight((sum,item)=>{
        return sum*10 + item;
    });
    
    console.log(result1)
    console.log(result2)
    

    二、 es5后新增的数组方法

    2.1、 数组的创建和初始化

    关于from和of,其实可能遇到call和bind,可以看看之前我的文章。

    • Array.from
      我们来看一组代码:
    var obj = {
        "0":"a",
        "1":"b",
        "2":"c",
        length: 3
    }
    var obj1 = {
        "0":"a",
        "1":"b",
        "2":"c",
    }
    var obj2 = {
        "a":"a",
        "b":"b",
        "c":"c",
        length: 3
    }
    
    console.log(Array.from(obj)); // [ 'a', 'b', 'c' ]
    console.log(Array.from(obj1)); // []
    console.log(Array.from(obj2)); // [ undefined, undefined, undefined ]
    

    从上面的代码,我们可以清楚的发现:
    1、 Array.from是需要length属性的,如果没有length属性,将返回空数组
    2、 Array.from操作对象的属性名一定是数字,不然会返回undefined。

    • Array.of

    Array.of主要用于将一组值转化成数组

    Array.of(3, 11, 8) // [3,11,8]
    Array.of(3) // [3]
    Array.of(3).length // 1
    

    Array.of的创建主要是为了弥补Array方法的一些小漏洞,例如:

    Array() // []
    Array(3) // [, , ,]
    Array(3, 11, 8) // [3, 11, 8]
    
    • fill

    fill方法的作用是,将给的定值填充到数组中,来看下面一组代码:

    ['a', 'b', 'c'].fill(7);  // [7, 7, 7]
    new Array(3).fill(7)  // [7, 7, 7]
    ['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
    let arr = new Array(3).fill({name: "jkb"});
    arr[0].name = "klivitam";
    console.log(arr); // [{name: "klivitam"}, {name: "klivitam"}, {name: "klivitam"}]
    

    从上面的代码我们可以发现:
    1、 fill接收三个参数,fill(填充的值,填充的开始值,填充的结束值)
    2、当只存在一个参数时,默认将全部数组都填充给列表项
    3、 当填充的值是对象的时候,我们是填充的对象的指针,所以这是一个坑 在用的时候请谨慎哦。

    2.2、 数组的查找

    • find/findIndex
    
    let data = [1,2,3,4,5];
    let obj = {
       name:"klivitam",
       number:1
    }
    let result1 =  data.find((it,index,arr)=>{
       return it>3;
    })
    let result2 = data.findIndex((it,index,arr)=>{
       return it>3;
    })
    let result3 =  data.find((it,index,arr)=>{
       return it>10;
    })
    let result4 = data.find(function(v){
       return v>this.number;
    },obj)
    console.log(result1) // 4
    console.log(result2) // 3
    console.log(result3) // undefined
    console.log(result4) // 2
    

    从面的代码我们可以发现:
    1、 find和findIndex分别都接收两个参数,第一个是函数,第二个是对象,前一个函数的this指针指向后一个对象
    2、 find和findIndex只能发现数组内出现的第一个值,如果没有发现返回undefined

    • includes
    let data = [1,2,3,4,5,NaN];
    
    console.log(data.includes(1)); // true
    console.log(data.includes(1,2)) // false
    console.log(data.includes(NaN)) // true
    console.log(data.indexOf(NaN)) // -1
    

    从面的代码我们就可以清楚的发现:
    1、 includes方法接收两个参数,第一个是搜索的值,第二个是从数据的第几项开始搜索
    2、 indexOf无法查找到NaN的位置,但是includes可以。

    • entries/keys/values
    let data = [1,2,3];
    for (let index of data.keys()) {
      console.log(index);
    }
    // 0
    // 1
    // 2
    
    for (let elem of data.values()) {
      console.log(elem);
    }
    // 1
    // 2
    // 3
    
    //这里使用了解构赋值
    for (let [index, elem] of data.entries()) {
      console.log(index, elem);
    }
    // 0 1
    // 1 2
    // 2 3
    

    2.2、 数组的操作

    • 扩展运算符
      扩展运算符(spread)是三个点(...)。它将一个数组转为用逗号分隔的参数序列。具体的事例代码如下:
    console.log(...[1, 2, 3]); // 1,2,3
    console.log((...[1, 2,3])); // Uncaught SyntaxError: Unexpected number
    Math.max(...[14, 3, 77]) // 77
    const [...[1,2,3]] = a1;  // 1,2,3
    [...['a', 'b'], ...['c'], ...['d', 'e']]; // [a,b,c,d,e]
    [...'hello'];// [h,e,l,l,0]
    

    关于扩展运算符这一块,我不想过多的来讲解,其实很多东西 还是要靠自己在写代码的时候来总结,关于有些技术,我也不知道哪里好,但是遇到问题了 我就想用。

    • copyWithin
    [1, 2, 3, 4, 5].copyWithin(0, 3); // [4, 5, 3, 4, 5]
    // 将3号位复制到0号位
    [1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5]
    
    [1, 2, 3, 4, 5].copyWithin(0, -2, -1); // [4, 2, 3, 4, 5]
    
    [].copyWithin.call({length: 5, 3: 1}, 0, 3)// {0: 1, 3: 1, length: 5}
    

    从上面的代码我们可以发现:
    1、 数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
    2、 该方法接受三个参数,分别是从该位置开始替换数据,从该位置开始读取数据,到该位置前停止读取数据。

    • flat/flatMap
    [1, 2, [3, 4]].flat(); // [1, 2, 3, 4]
    
    [1, 2, [3, [4, 5]]].flat(); // [1, 2, 3, [4, 5]]
    
    [1, 2, [3, [4, 5]]].flat(2); // [1, 2, 3, 4, 5]
    
    [1, [2, [3]]].flat(Infinity)// [1, 2, 3]
    
    [1, 2, , 4, 5].flat() // [1, 2, 4, 5]
    
    [2, 3, 4].flatMap((x) => [x, x * 2])// [2, 4, 3, 6, 4, 8]
    

    从上面的代码我们可以发现:

    • flat的作用是:用于将嵌套的数组“拉平”,变成一维的数组。flapmap的作用是首先将元素进行一次map方法,然后再进行一次flat方法。
    • flat方法接受一个参数,代表拉伸的层数,也可以用Infinity,拉平所有的嵌套数组
    • flat再拉平数组的时候,然后遇到空位会跳过该空位

    三、数组的空位

    前面在讲解flat的时候,我们提到了一个空位的概念,结合开发以及阮大神在博客中的总结,我也更深的了解到空位的差异。
    首先我们来看一段代码:

    [,'a'].forEach((x,i) => console.log(i)); // 1
    ['a',,'b'].filter(x => true) // ['a','b']
    [,'a'].every(x => x==='a') // true
    [1,,2].reduce((x,y) => x+y) // 3
    [,'a'].some(x => x !== 'a') // false
    [,'a'].map(x => 1) // [,1]
    [,'a',undefined,null].join('#') // "#a##"
    [,'a',undefined,null].toString() // ",a,,"
    

    这里我们就可以明显的看到在es5中对空格的处理方法就已经出现了分歧。

    • forEach(), filter(), reduce(), every() 和some()都会跳过空位。
    • map()会跳过空位,但会保留这个值
    • join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。

    那我们来看es6呢?来看下面一段代码:

    Array.from(['a',,'b'])// [ "a", undefined, "b" ]
    [...['a',,'b']] // [ "a", undefined, "b" ]
    [,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
    new Array(3).fill('a') // ["a","a","a"]
    let arr = [, ,];
    for (let i of arr) {
      console.log(1);
    }
    // 1
    // 1
    [...[,'a'].entries()] // [[0,undefined], [1,"a"]]
    [...[,'a'].keys()] // [0,1]
    [...[,'a'].values()] // [undefined,"a"]
    [,'a'].find(x => true) // undefined
    [,'a'].findIndex(x => true) // 0
    

    从上面代码我们可以发现:

    • Array.from、扩展运算符、entries()、keys()、values()、find()和findIndex()方法会将数组的空位,转为undefined
    • fill()会将空位视为正常的数组位置。
    • copyWithin()会连空位一起拷贝。
    • 上面代码中,数组arr有两个空位,for...of并没有忽略它们。如果改成map方法遍历,空位是会跳过的。

    其实用代码演示了这么多,无非就是想说明一件道理:空位不同的方法对其的处理方式不同 如果你能够记得所有方法的处理方式,那你大可以任意使用 如果不行 还是尽量避免出现空位吧

    说在最后

    这篇文章写的我很是烦躁呀,主要总结下来东西实在是太多了,但是怎么说呢?还是有很大的好处的,比如数组的空位这个之前我就只知道基础的几种,剩下的 我基本都没有去验证,现在抽时间把这些东西全部验证了一遍感觉印象也加深了不少。好了 差不多一点钟了,去睡觉了 (说好的从这周起开始睡美容觉的)

    相关文章

      网友评论

        本文标题:从头开始复习js之让你彻彻底底搞清楚数组

        本文链接:https://www.haomeiwen.com/subject/qookgqtx.html