JS数组

294 阅读5分钟

数组对象——一种特殊的对象

JS 其实没有真正的数组,只是用对象模拟数组

JS 的数组不是典型的数组

典型的数组

  • 元素的数据类型相同
  • 使用连续的内存存储
  • 通过数字下标获取元素

但 JS 的数组不这样

  • 元素的数据类型可以不同
  • 内存不一定是连续的(对象是随机存储的 0
  • 不能通过数字下标,而是通过字符串下标
  • 这意味着数组可以有任何 key
  • 比如
  • let arr=[1,2,3]
  • arr['xxx']=1

创建一个数组

新建

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

转化

  • let arr='1,2,3'.split(',')
  • let arr='123'.split('')
  • Array.from('123')

伪数组

  • let divList=document.querySelectorAll('div')
  • 伪数组的原型链中并没有数组的原型
  • 可以通过 let divArray=Array.from(divList)转化

没有数组共用属性的[数组],就是伪数组

每次你拿到伪数组,要做的第一件事就是把它转化成数组

创建一个数组(续)

合并两个数组,得到新数组

  • arr1.concat(arr2)

截取一个数组的一部分,不改变原来数组

  • arr1.slice(1)//从第二个元素开始
  • arr1.slice(0)//全部截取,常用来复制一个数组
  • 注意,JS 只提供浅拷贝。所有 JS 原生提供的东西都是浅拷贝

增删改查——数组中的元素

删元素

跟对象一样

  • let arr=['a','b','c']
  • 'delete arr['0']
  • arr//[empty,'b','c']
  • 神奇,数组的长度并没有遍
  • 只有长度,一个下标都没有,这种数组叫做稀疏数组。稀疏数组没有任何好处,它只有 bug。
  • 实际上,这种删法是对象的删法,不太适合数组

如果直接改 length 可以删元素吗

  • let arr=[1,2,3,4,5];
  • arr.length=1
  • 我 X,居然可以?!
  • JS 真神奇
  • 重要:不要随便改 length。改了不会报错,遇到 bug 你只能自己解决了。

删除头部的元素

  • arr.shift()//arr 被修改,并返回被删元素

删除尾部的元素

  • arr.pop()//arr 被修改,并返回被删元素

删除中间的元素

  • arr.splice(index,1)//删除 index 的一个元素
  • arr.splice(index,1,'x')//并在删除位置添加'x'
  • arr.splice(index,1,'x','y')//并在删除位置添加'x','y'

查看所有元素

查看所有属性名

  • let arr=[1,2,3,4,5];arr.x='xxx'
  • Object.keys(arr)
  • for(let key in arr){console.log(${key}:${arr[key]})}
  • 上面的方法都只适合对象不适合数组

查看数字(字符串)属性名和值

for(let i=0;i<arr.length;i++){
  console.log(`${i}:${i}`)
}
  • 你要自己让 i 从 0 增长到 length-1。
  • for 循环是访问数组的吧比较常见的形式,不要用 for in,for in 是用来访问对象的。
  • 也不要用 Object.keys,它可能会获取到你不想要的东西
arr.forEach(function(item,index){
  console.log(`${index}:${item}`)
})
  • 也可以用 forEach/map 等原型上的函数

面试:请聊一聊 for 循环和 forEach 的区别。

第一个区别:上面两种大部分情况都是通用的,只有一种情况不通用,就是 for 循环里面有 break 和 continue 的时候,而 forEach 是不支持的,因为 forEach 是个普通的函数,而 for 循环是个关键字,关键字的功能可能会更强大一点。

第二个区别:for 循环是个关键字,不是个函数,所以 for 循环只有块级作用域,没有函数作用域。但是 forEach 是个函数,所以它里面是函数作用域。一个是块,一个是函数。

forEach 是一个槛

自己写 forEach 才能理解 forEach

function forEach(array,fn){
  for(let i=0;i<array.length;i++){
    fn(array[i],i,array)
  }
}
  • forEach 用 for 访问 array 的每一项
  • 对每一项调用 fn(array[i],i,array)
  • 为什么要传入 array 呢?不为什么,规定如此。

查看单个属性

跟对象一样

  • let arr=[111,222,333]
  • arr[0]

索引越界

  • arr[arr.length]===undefined
  • arr[-1]===undefined

举例

for(let i=0;i<=arr.length;i++){
  console.log(arr[i].toString())
}
  • 报错:Cannot read property 'toString' of undefined
  • 任何不存在的下标你去读都会得到 undefined,undefined 不是一个对象,不是一个对象那么它就没有 toString 属性。当你看到上面这句报错的话,很有可能你是数组越界了
  • 如何解决这个问题,用 console.log 调试大法,它说哪个东西是 undefined,你就把哪个东西打印出来

查找某个元素是否在数组里

  • arr.indexOf(item) // 存在返回索引,不存在则返回-1

使用条件查找元素

  • arr.find(item=>item%2===0) //找第一个偶数
  • 可以找到符合这个条件的第一个元素

使用条件查找元素的索引

  • arr.findIndex(item=>item%2===0) // 找第一个偶数的索引
  • 可以找到符合这个条件的第一个元素的索引

增加数组中的元素

在尾部增加元素

  • arr.push(newItem) // 修改 arr,返回新长度
  • arr.push(item1,item2) // 修改 arr,返回新长度

在头部增加元素

  • arr.unshift(newItem) // 修改 arr,返回新长度
  • arr.unshift(item1,item2) // 修改 arr,返回新长度

在中间添加元素

  • arr.splice(index,0,'x') //在 index 处插入'x'
  • arr.splice(index,0,'x','y') //在 index 处插入'x','y'或者更多元素

修改数组中的元素

反转顺序

  • arr.reverse() // 修改原数组
  • 反转一个字符串:`s.split('').reverse().join('')

自定义顺序

  • arr.sort((a,b)=>a-b)
  • 例:
let arr=[
  {name:'小明',score:99},{name:'小红',score:95},{name:'大黄',score:100}
]
arr.sort((a,b)=>a.score-b.score)

数组变换

  • 此三个函数都会返回一个新数组,不改变原数组

map:n 变 n

map 的意思是一一映射

let arr=[1,2,3,4,5,6]
arr.map(item=>item*item) //把arr的每一项一一映射,变成它的平方

filter:n 变少

let arr=[1,2,3,4,5,6]
arr.filter(item=>item%2===0?true:false)
// 返回偶数。

简写:

let arr=[1,2,3,4,5,6]
arr.filter(item=>item%2===0)

reduce:n 变 1

let arr=[1,2,3,4,5,6]
arr.reduce((sum,item)=>{return sum+item},0)//return的值作为下一次的结果

把 reduce 当 map 用

let arr=[1,2,3,4,5,6]
arr.reduce((result,item)=>{return result.concat(item*item),[]})
//返回一个新数组,每一个元素是原来数组元素的平方

把 reduce 当 filter 用

let arr=[1,2,3,4,5,6]
arr.reduce((result,item)=>item%2===1?result:result.concat(item))

上面可以简写:

let arr=[1,2,3,4,5,6]
arr.reduce((result,item)=>
  result.concat(item%2===1?[]:item)
,[])
  • reduce 是数组里功能最强大的 API,有了 reduce 你不需要 map,map 只是一个语法糖而已

把 splice 理解了,reduce 理解了,数组就完全理解了