JS基础--JS数组

424 阅读8分钟

典型数组与JS数组

典型的数组

  • 典型的数组中的元素数据类型相同

  • 典型的数组使用连续的内存存储

  • 通过数字下标获取元素

JS的数组

  • 元素的数据类型可以不同

  • 内存的存储不一定是连续的(可以是:0,1,2;也可以是0,2,1)

  • 获取元素通过的是字符串,而非数字下标(因为数组其实就是一个比较特别的对象)

  • 数组可以拥有任意的key,不一定只是数字(实际上是字符串)

    • let arr  = [1,2,3]
      arr['xxx'] = 4
      console.log(arr) // [1, 2, 3, xxx: 1],只有数字的属性名会自动隐藏
      

JS数组的实质

  • JS数组实际上就是个拥有特定结构的对象,属性名都是数字的形式(类型其实还是字符串),还有一个叫 length 的属性
  • 在使用数组的过程中我们可以发现其身上都是对象的影子

JS的伪数组

  • 拥有类似数组的结构,但是原型链中并没有Array的原型的对象,称之为伪数组
  • let divList = document.querySelectorAll('div')这里生成的是一个节点列表,但是原型链中并没有Array的原型。

JS数组的创建

创建一个数组

  1. let arr = [1,2,3]这是数组的简单创建方式,可以感觉到和 {} 创建对象有异曲同工之妙。
  2. let arr = new Array(1,2,3)这是数组的规范创建方式,直接传入数组的元素作为参数(传入的元素数量最少2个,否则会变成下面3.的情况)。
  3. let arr = new Array(3)当传入的参数只有一个的时候指定的是创建数组的长度。

非数组转化成数组

  1. 通过字符串的 split() 方法将字符串转化成数组。

    let arr = '1,2,3'.split(',') // 通过 , 号将字符串分割 ["1","2","3"]
    let arr = '123'.split('') // 通过空字符串将字符串分割 ["1","2","3"]
    
  2. Array.from()这个方法可以将具有类似数组结构的对象或字符串转换成数组,除了属性名是数字以外,还要有一个length的属性来确定转换后的数组的长度。这个方法最常用于将伪数组变成真数组

    Array.from({"1":"a","2":"b","3":"c","ss":"ss",'length':5}) 
    // [undefined, "a", "b", "c", undefined]
    

数组修改成新数组

  1. arr1.concat(arr2)将两个数组合并(arr1在前,arr2在后),返回一个新的数组,arr1、arr2没有被修改。

  2. 截取一个数组的一部分,形成一个新数组。

    let arr1 = [1,2,3,4,5,6]
    let arr2 = arr1.slice(2,3) // [3]
    let arr3 = arr1.slice(2) // [3,4,5,6]
    let arr4 = arr1.slice(0) // 全部截取,相当于复制一个数组
    // 第一个参数是截取的起始索引(截取内容包括起始点);第二个参数是截取的终点(截取内容不包括终点),默认是arr1.length截取至末尾。
    
  3. 注意:所有JS原生API都只提供浅拷贝,即只拷贝了对象的引用地址。

    // 以 Array.prototype.slice()为例
    let arr = [{'name':'Jack',age:18},
               {'name':'Tom',age:19},
               {'name':'Lich',age:20}
              ]
    let arr2 = arr.slice(0)  // 复制一份arr给arr2
    arr[0].name = 'John'
    arr[0].name // John
    arr2[0].name // John 而不是 Jack
    

JS数组的增删查改

删元素

2种虚假的删除元素

  1. 像对象一样,通过delete来删除数组的中的元素

    let arr = ['a','b','c']
    delete arr[0]
    arr // [empty,'b','c'] 删除了元素的内容,length没有改变,仍然是3个元素。
    

    当一个数组中实际的元素与length并不匹配,我们就将它称作稀疏数组

    显然delete删除数组元素的结果并不是我们想要的。

  2. 既然1.中的方法没有改变length,那把length也改了行不行?

    let arr = ['a','b','c']
    arr['length']  = 1
    arr // ['a'] 可以发现length也是可以修改的
    let arr2 = ['a','b','c']
    delete arr2[1] // 尝试使用delete并配合length的修改来看能否达到想要的效果
    arr2['length'] = 2  
    arr2  // ['a',empty] 然而不能
    

    修改length可以影响数组的长度,从而影响到元素,但这也不能达到理想的结果

    而且不要随意修改length

真实的删除元素

  1. arr.shift() 删除最前面的元素,并返回被删除的元素,原来的数组被修改。

  2. arr.pop()删除最后面的元素,并返回被删除的元素,原来的数组被修改

  3. splice()方法从数组中间删除元素,这个方法也可以实现shift() pop()的效果

    arr.splice(index,number) // 从arr的索引为index的元素开始,往后删除number个元素,并返回删除的元素
    arr.splice(index,number,'x','y') // 在删除number个元素后,在删除的位置插入新元素'x','y'
    let arr = ['a','d','e','c']
    arr.splice(1,2,'b','f')
    arr // ['a','b','f','c']
    

查看元素

查看所有的元素

  1. Object.key()与for in 循环

    因为数组也是对象,所以我们可以使用对象的方法,Object.keys(arr)来查看我们数组的所有的属性名。或者使用 for in 循环进行遍历。

    let arr =  [1,2,3,4,5,6]
    arr['x'] = 'xxx' 
    Object.keys(arr) // ["0", "1", "2", "3", "4", "5", "x"] 
    for(let i in arr){
        console.log(`${i}:${arr[i]}`)
    }
    

    上面的两种方法会遍历所有的属性名,包括我们乱给的 'x' 这个属性名,但有时候我们只希望获得那些数字的属性名。

  2. 遍历数组

    • 传统的遍历数组
    for(let i = 0;i<arr.length;i++){ // 注意i的范围
        console.log(`${i}:${arr[i]}`)
    }
    // 这样我们就只遍历了那些数字的属性名,而不会遍历出不符合规则的属性的名。
    
    • forEach循环遍历数组,及其原理
    // forEach的回调函数的第一个参数是当前循环的数组的值,第二个参数是当前循环的数组的索引
    arr.forEach(function(item,index){ 
        console.log(`${index}:${item}`)
    })
    // forEach循环也不会遍历出非数字形式的属性名
    // forEach的原理:
    function forEach(array, fn) {
        for (let i = 0; i < array.length; i++) {
            fn(array[i], i, array)
        }
    }
    
    • for循环与forEach的最大区别

      for循环里面可以有break和continue 而 forEach中没有。

      for循环是一个关键字,只有块级作用域,而forEach是一个函数是一个函数作用域 。

查看与查找单个元素

  1. 正常查看与索引越界

    • 通过arr[index]即可查看到对应索引的属性值,注意index最后会被转换成字符串

    • 索引越界:

      索引越界及查看的索引并不在数组的索引范围内

      arr[-1] // undefined
      arr[arr.length] // undefined
      
  2. 查找数组中的属性

    // 1. 查找某个元素是否在数组里面
    arr.indexOf(item) // 存在,则返回索引值;不存在,则返回-1
    
    // 2.使用条件查找元素
    arr.find((item)=>{return item%2===0}) // 返回数组中的第一个偶数元素
    // 遍历数组,当函数第一次返回true的时候,将正遍历的数组的元素返回
    
    // 3.使用条件查找元素的索引
    arr.findIndex((item)=>{return item%2===0}) // 返回数组中的第一个偶数元素的索引
    

增加与修改数组中的元素

增加元素

// 1.在尾部加元素
arr.push(newitem) // 修改arr,元素增加,length也会增加。返回数组新的长度。

// 2.在头部加元素
arr.unshift(newitem) // 修改arr,元素增加,length也会增加。返回数组新的长度。

// 3.在中间添加元素 (删除和添加都是使用splice方法)
arr.splice(index,0,'newitem1','newitem2') // 删除元素数量为0即不删除,直接插入新元素
  • 上面的方法和arr[index] = newitem的区别:

    上面的操作不会产生稀疏数组,而arr[index] = newitem这种操作容易产生稀疏数组,所以增加元素都使用上面的3种方法

修改元素

  • 直接arr[index] = 'xxx'即可修改元素。

数组变换

  1. 反转数组,reverse

    let arr = [1,2,3,4,5]
    arr.reverse() // [5, 4, 3, 2, 1],反转数组,并返回反转后的数组
    
  2. 按自定义要求整理数组,sort

    • 当不传入回调函数的时候,默认将元素换为字符串,然后比较它们的UTF-16代码单元值序列
    • 当传入回调函数的时候,按照要求的比对进行数组整理
    let arr = [1,2,3,4,5]
    arr.sort(function(a,b){ // 第一个参数是前面用于比较的元素,第二个参数是后面用于比较的元素
        return -1 // 根据返回值:负数(-),a在b前;0,ab位置不变;正数(+),b在a前面
    })
    
  3. 整体变换,map

    • 原数组没有被修改。将原数组每一个元素都执行一次函数,并返回一个包含所有元素执行函数后的结果的数组。
    let arr = [1,2,3,4,5]
    arr.map(x=> x*2) // [1,4,6,8,10]
    arr // [1,2,3,4,5]
    
  4. 过滤数组,filter

    • 原数组没有被修改。创建一个新的空数组,将原数组元素从头开始,执行函数,若返回true则将该元素放入新的数组,最终返回该新的数组。
    let arr = [1,2,3,4,5]
    arr.filter(x=> x%2===0) // [2,4] 筛选数组中的偶数
    arr // [1,2,3,4,5]
    
  5. 结果汇总,reduce

    • 原数组每个元素执行一次回调函数,并根据函数将结果汇总为一个返回值。
    • “一个返回值”不定就只是一个值,也可以是一个包含多个对象的数组[{},{},{}]
    let arr = [1,2,3,4,5]
    // reduce函数:第一个参数是回调函数;第二个参数是累计值的初始值,如果没有则使用数组的第一个元素作为累计值的初始值。
    let sum = arr.reduce(function(result,item){ // 回调函数:第一个参数是累计值;第二个参数是当前值
        if(item%2===0){
            return result+=item
        }else{
            return result
        }
    },0) 
    sum = 6 // 求数组中的偶数和