【JS全解】JS数组

103 阅读5分钟

数组对象

  • JS中没有真正的数组,只是用对象模拟一个数组。在JS中数组是一种特殊的对象。
  • JS数组的特色
    1. 数组的元素数据类型可以是不同的。
    2. 数组内部,元素的内存存储是随机存储的(而不是连续的)。
    3. index不是数字下标,而是通过字符串下标。这也意味着数组可以有任何key。

新建数组

var arr = [1, 2, 3]
var arr = new Array(1, 2, 3)
var arr = new Array(3)

转化成数组

var arr = '1,2,3'.split(',') // 一个'1,2,3',字符串中以','来分割每个元素
var arr = '123'.split('')
Array.from('123')

伪数组

let divList = document.querySelectorAll('div')
divList.__proto__ === Array.prototype // false

let divArray = Array.from(divList)
divArray.__proto__ === Array.prototype // true
  • 伪数组:没有数组共有属性的“数组”。
  • 伪数组的原型链中没有数组的原型。
  • 伪数组的原型直接指向了Object.prototype。
  • 伪数组也没有push()、pop()这类数组对象的共有属性。
  • 伪数组可以通过Array.from()方法转化为数组。

合并数组

  • 利用concat()方法,合并两个数组,返回一个新的数组。
    let arr1  = [1, 2, 3]
    let arr2 = [4, 5]
    arr3 = arr1.concat(arr2)
    arr3 // [1, 2, 3, 4, 5]
    
  • concat()的参数也可以是一个对象或者简单数据类型,即把它看为一个元素只有一个的数组。

截取数组

  • 利用slice()方法,截取一个数组的一部分。
    let arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    let arr2 = arr1.slice(2) // [3, 4, 5, 6, 7, 8, 9]
    
  • arr.slice(start,end),start表示从第几位开始截取,end表示到第几位结束截取。
  • 注意
    • JS只提供浅拷贝。

数组的增删改查

增加元素

在头部增加元素:unshift()

arr.unshift(newItem)
arr.unshift(item1, item2)
  • 在头部添加元素,返回值为新的数组长度。

在尾部增加元素:push()

arr.push(newItem)
arr.push(item1, item2)
  • 在尾部添加元素,返回值为新的数组长度。

在中间增加元素:splice()

arr.splice(index, 0, 'x') // 在index处插入'x'
arr.splice(index, 0, 'x', 'y') // 在index处插入'x','y'
  • (index, 0, 'x')
    • index为插入元素的位置。
    • 0表示不删除元素。
    • 'x'表示插入字符串'x'。
  • splice()的返回值为删除的元素的数组,但在增加元素时,没有删除的元素,因此返回值为一个空的数组。

删除元素

  • 使用delete关键字
    let arr = ['a', 'b', 'c']
    delete arr['0']
    arr // [empty, 'b', 'c']
    
    • 使用delete的话,数组的长度不变
  • 修改数组的.length,可能会删除数组元素。
    let arr = ['a', 'b', 'c']
    arr.length = '2'
    arr //  ['a', 'b']
    
    • 因此不要随意修改数组的.length属性。

删除头部的元素:shift()

let arr = ['a', 'b', 'c']
arr.shift() // 'a'
arr // ['b', 'c']
  • 从数组中删除第一个元素,并返回该元素的值。

删除尾部元素:pop()

let arr = ['a', 'b', 'c']
arr.pop() // 'c'
arr // ['a', 'b']

删除指定元素:splice()

let arr = [1, 2, 3, 4, 5, 6]
arr.splice(1,3) // [2, 3, 4]
arr // [1, 5, 6]
  • 返回值为删除的元素的数组。
  • 语法:array.splice(start, deleteCount,item)
    • start:删除的开始位置。
    • deleteCount:可选。表示要删除的元素的个数,若省略则默认删除从start开始的所有元素。
    • item:可选,可添加多个。表示在删除的位置添加的新元素。

查看元素

查看所有属性名和属性值

  • Object.keys()和Object.values()
    let arr = [1, 2, 3, 4, 5, 6]
    arr.x = 'x'
    Object.keys(arr) // ['0', '1', '2', '3', '4', '5', 'x']
    Object.values(arr) // [1, 2, 3, 4, 5, 6, 'x']
    
  • for in
    let arr = [1, 2, 3, 4, 5, 6]
    arr.x = 'x'
    for(i in arr){
    	console.log(i)
    }
    
  • 这两种方法会把非数字的下标一起打印。

查看所有数字属性名和值

  • 直接使用for循环
    let arr = [1, 2, 3, 4, 5, 6]
    for(let i = 0; i < arr.length; i++){
    	console.log(`${i}${arr[i]}`)
    }
    
  • 使用Array().prototype自带的forEach()/map()函数:
    let arr = [1, 2, 3, 4, 5, 6]
    arr.forEach(function(item, index){
    	console.log(`${index}${item}`)
    })
    
    • index为下标。
    • item为元素。
    • forEach()本质是一个for循环,内部使用了回调,会自动把下标包装为index,把元素包装为item。
      function forEach(arr, fn) {
      	for(let i = 0; i < arr.length; i++){
      		fn(i, arr[i]) // i就是index,arr[i]就是item
      	}
      }
      
  • 注意
    • for是一个关键字,forEach()是一个普通函数,后者不适用于break。forEach()一旦开始,就必须要全部循环完。
    • for后面的花括号代表的是块级作用域,而forEach()后面的花括号代表的是函数作用域。

查看单个属性

  • 用下标调用
    let arr = [1, 2, 3, 4, 5, 6]
    arr[0]
    
    • 索引越界
      arr[arr.length] === undefined
      arr[-1] === undefined
      
      • 调用任何不存在的下标的元素,都会输出undefined。
  • 查看元素是否在数组里面:indexOf()
    arr.indexOf(item) 
    
    • 存在则返回下标,否则返回**-1**。
  • 使用条件查找元素,返回元素:find()
    let arr = [1, 2, 3, 4, 5, 6]
    arr.find(item => item %2 ===0) // 找到偶数
    arr.find(function(item){
    	return x % 5 === 0
    }) // 找到5的整数倍
    
    • 它的返回值为元素。
    • 只会找到第一个符合条件的元素。
  • 使用条件查找元素,返回下标:findIndex()

修改元素

  • 直接利用下标来修改。
  • 反转顺序
    arr.reverse()
    
    • 这种修改方式会直接对原数组进行操作。
    • 把字符串颠倒方向:
      let str = 'abcde'
      /* let arr = Array.from(str)
      str = arr.reverse().join('') */
      str = Array.from(str).reverse().join('')
      
  • 自定义排列
    arr.sort(function(a, b){
    	return a-b
    })
    
    • JS默认情况下,数值小的理应排在前面(顺序排列)。
    • 顺序排列:function(a, b){return a - b}
    • 逆序排列:function(a, b){return b - a}
    • 用sort()来排列非数值型的元素:
    let arr = [
        {name:'小明', score:90},
        {name:'小红', score:70},
        {name:'小黄', score:80}
    ]
    
    arr.sort((a, b) => b.score - a.score)
    

数组变换

map()

  • n变n
  • arr.map(fn())
    • arr为原数组。
    • fn()为变换规则。
  • 把数字变成星期:
    let arr = [0,1,2,2,3,3,3,4,4,4,4,6]
    let arr2 = arr.map(item => {
        switch(item){
            case 0: 
                return '周日'
            case 1: 
                return '周一'
            case 2: 
                return '周二'
            case 3: 
                return '周三'
            case 4: 
                return '周四'
            case 5: 
                return '周五'
            case 6: 
                return '周六'
        }
    })
    console.log(arr2) // ['周日', '周一', '周二', '周二', '周三', '周三', '周三', '周四', '周四', '周四', '周四','周六']
    

filter()

  • n变少
  • arr.filter(fn())
    • arr为原数组。
    • fn()为过滤规则。
  • 找出所有大于 60 分的成绩:
let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
scores.filter(score => score >=60)

reduce()

  • n变1
  • arr.reduce(fn(result, item),0)
    • arr为原数组。
    • fn()为变换规则,它最后一次的输出值即为reduce()的最终结果。
    • result为结果。每一次fn()的return结果都会赋值给result。
    • item为arr中每一项。
    • 0为result的初始值。
  • 算出所有奇数之和:
    let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
    scores.reduce((sum, item) => item % 2 !== 0 ? sum + item: sum, 0)
    
  • reduce()的结果可以不是“1”,而可以是多个结果——数组。
    • 利用reduce()找出所有大于 60 分的成绩:
      let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
      scores.reduce((arr, item) => item >= 60? arr.concat(item): arr, [])
      

拓展

深拷贝和浅拷贝

  • 深拷贝:拷贝时,在内存中开辟一个新的空间,并在该空间内复制一份拷贝的对象。
  • 浅拷贝:拷贝时,只是复制一份引用对象的地址。

稀疏数组

  • 只有长度,没有元素,内容为empty的数组。

返回值

  • arr[0]为例。
  • arr之所以能作为数组第一个数,并对它进行操作,本质原因是:arr[0]是arr的一个下标为1的属性,能够被直接调用。