Skip to content

数组

声明

创建一个空数组有两种语法:

js
let arr = new Array()
let arr = []

数组元素从 0 开始编号, 我们可以将元素的索引值填写在方括号内来获取元素:

js
const user = ['Tom', 'Jack', 'Rose']
console.log(user[0]) // Tom
console.log(user[1]) // Jack
console.log(user[2]) // Rose

length 属性的值是数组中元素的总个数

js
console.log(user.length) // 3

数组可以存储任何类型的元素。

js
const arr = [1, 'a', { name: 'Lucy' }, function(){console.log('hello')}]
console.log(arr[2].name) // Lucy
arr[3]() // hello

数组方法

JavaScript 中的数组既可以用作队列,也可以用作栈。它们允许从前端/末端来添加/删除元素。

队列 (FIFO 先进先出)

  • push 在末端添加一个元素
  • shift 取出队列最前端的一个元素

队列

栈 (LIFO 后进先出)

  • push 在末端添加一个元素
  • pop 从末端取出一个元素

栈

作用于数组末端的方法

  • pop (取出数组的最后一个元素)
js
const fruits = ['apple', 'orange', 'banana'] 
console.log(fruits.pop()) // banana
console.log(fruits) // [ 'apple', 'orange' ]
// pop 方法返回被删除的元素
  • push (在数组末端添加元素)
js
const fruits = ['apple', 'orange', 'banana'] 
console.log(fruits.push('pear')) // 4
console.log(fruits) // [ 'apple', 'orange', 'banana', 'pear' ]
// push 方法返回数组的长度

作用于数组前端的方法

  • shift (取出数组的第一个元素)
js
const fruits = ['apple', 'orange', 'banana'] 
console.log(fruits.shift()) // apple
console.log(fruits) // [ 'orange', 'banana' ]
// shift 方法返回被删除的元素
  • unshift (在数组的前端添加元素)
js
const fruits = ['apple', 'orange', 'banana'] 
console.log(fruits.unshift('test')) // 4
console.log(fruits) // [ 'test', 'apple', 'orange', 'banana' ]
// unshift 方法返回数组的长度

pop/push, shift/unshift 四个方法都会改变原数组

其他方法

  • splice (在数组中添加,删除和插入元素)

语法: arr.splice(index[, deleteCount, elem1, ..., elemN])

从 index 开始:删除 deleteCount 元素并在当前位置插入 elem1, ..., elemN。最后返回已删除元素的数组。

js
/* demo1 删除元素 */
const words = ['a', 'b', 'c']
console.log(words.splice(0, 1)) // [ 'a' ]
console.log(words) // [ 'b', 'c' ]

/* demo2 删除并插入元素 */
const words = ['a', 'b', 'c']
console.log(words.splice(1, 2, 'd', 'e')) // [ 'b', 'c' ]
console.log(words) // [ 'a', 'd', 'e' ]

/* demo3 插入元素 */
const words = ['a', 'b', 'c']
console.log(words.splice(1, 0, 'd', 'e')) // []
console.log(words) // [ 'a', 'd', 'e', 'b', 'c' ]

// splice 方法改变原数组

允许负向索引 (从数组末尾计算位置)

js
const words = ['a', 'b', 'c']
console.log(words.splice(-1, 0, 'j', 'h')) // []
console.log(words) // [ 'a', 'b', 'j', 'h', 'c' ]
  • slice (从已有的数组中返回选定的元素)

语法: arr.slice(start, end)

返回一个新的数组,包含从 start 到 end (不包括该元素)的原数组中的元素。

js
const arr = ['h', 'e', 'l']
console.log(arr.slice(0, 2)) // [ 'h', 'e' ]
console.log(arr) // [ 'h', 'e', 'l' ]

// slice 方法不改变原数组

如果 end 未被规定,那么 slice 方法会选取从 start 到数组结尾的所有元素

允许负向索引

  • concat (将数组与其他数组或元素合并)

语法: arr.concat(arg1, arg2...)它接受任意数量的参数 — 数组或值

js
const nums = [1,2,3]
console.log(nums.concat([4,5])) // [ 1, 2, 3, 4, 5 ]
console.log(nums) // [ 1, 2, 3 ]

console.log(nums.concat([4,5], 6, 7)) // [ 1, 2, 3, 4, 5, 6, 7 ]

console.log(nums.concat(6, 7)) // [ 1, 2, 3, 6, 7 ]

// concat 方法不改变原数组

对数组中的每个元素依次执行所提供的回调函数(升序执行)

语法: arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

callback 接收四个参数

  • accumulator 上一次调用回调时返回的累积值, 或是initialValue的值
  • currentValue 数组中正在处理的元素
  • currentIndex 数组中正在处理的元素的索引(如果提供了initialValue,则起始索引号为0,否则为1)
  • array 调用reduce的数组

initialValue

  • 作为第一次调用 callback函数时的第一个参数的值。 如果不传,则使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

MDN详解

js
const arr = [1, 3, 5]
/* 不传 initialValue */
const res = arr.reduce((prev, cur, index, array) => {
  console.log(prev, cur, index, array)
  return prev + cur
})
console.log(res)
console.log(arr) 
/**
  1 3 1 [ 1, 3, 5 ] 
  4 5 2 [ 1, 3, 5 ]
  9
  [ 1, 3, 5 ]
 */

/* 传 initialValue */
const res = arr.reduce((prev, cur, index, array) => {
  console.log(prev, cur, index, array)
  return prev + cur
}, 2)
console.log(res)
console.log(arr) 
/**
  2 1 0 [ 1, 3, 5 ]
  3 3 1 [ 1, 3, 5 ]
  6 5 2 [ 1, 3, 5 ]
  11
  [ 1, 3, 5 ]
 */

查询数组

indexOf/lastIndexOf 和 includes

  • arr.indexOf(item, from) 从索引 from 查询 item,如果找到返回索引,否则返回 -1
  • arr.lastIndexOf(item, from) — 和上面相同,只是从尾部开始查询。
  • arr.includes(item, from) — 从索引 from 查询 item,如果找到则返回 true。
js
const arr = [0, 1, false, '1']
console.log(arr.indexOf(0)) // 0
console.log(arr.indexOf(false)) // 2
console.log(arr.indexOf(1)) // 1
console.log(arr.indexOf('1')) // 3
console.log(arr.indexOf(null)) // -1

console.log(arr.includes(false)) // true

这些方法使用 === 比较。例如查询 false,会精确到是 false 而不是零

includes 与 indexOf/lastIndexOf 的差别是它能正确处理NaN

js
const arr = [NaN]
console.log(arr.indexOf(NaN)) // -1 期望返回0
console.log(arr.includes(NaN)) // true

find()

语法:

js
arr.find(function(item, index, array){
  // 如果查询到返回 true , 查询停止
})

该函数对数组中的每个元素重复调用

  • item 是元素
  • index 是索引
  • array 是数组本身
js
const users = [
  {id: 1, name: 'Tom'},
  {id: 2, name: 'Jack'},
  {id: 3, name: 'Lucy'}
]
let user = users.find(item => item.id === 1)
console.log(user) // { id: 1, name: 'Tom' }

find 方法查询的是使函数返回 true 的第一个元素

filter()

语法

js
arr.filter(function(item, index, array){})

语法与 find 大致相同,返回的是所有匹配元素组成的数组

js
let user = users.filter(item => item.id > 1)
console.log(user) // [ { id: 2, name: 'Jack' }, { id: 3, name: 'Lucy' } ]

二分法查询

  • 需符合条件: 数组必须是从小到大有序排列的
  • 当所要查询的值大于中间值,说明要查找的值可能存在于arr[mid+1]到arr[end]之间,所以start = mid+1
  • 当所要查询的值小于中间值,说明要查找的值可能存在于arr[start]到arr[mid-1]之间,所以end = mid-1
js
function search(arr, target) {
  let start = 0
  let end = arr.length - 1

  while(start <= end) {
    // 取中间值下标
    let mid = Math.floor((start + end) / 2)

    if (target > arr[mid]) {
      start = mid + 1
    } else if (target < arr[mid]) {
      end = mid - 1
    } else {
      return mid
    }
  }

  return -1
}

转换数组

map()

语法

js
arr.map(function(item, index, array){
  // 返回新值而不是当前元素
})

它对数组中每个元素调用函数并返回符合结果的数组

js
const nums = [1, 2, 3, 5]
let newNums = nums.map(item => item*2)
console.log(newNums) // [ 2, 4, 6, 10 ]
console.log(nums) // [ 1, 2, 3, 5 ]

sort()

语法

js
function compare(a, b){
  if (a > b) return 1
  if (a == b) return 0
  if (a < b) return -1
}
arr.sort(compare)

接收一个比较函数来确认排序方式, 正数表示更大,而负数表示更小

js
const users = [
  {id: 3, name: 'Tom'},
  {id: 1, name: 'Jack'},
  {id: 2, name: 'Lucy'}
]
/* 正序 */
let newUsers = users.sort((x, y) => x.id - y.id)
console.log(newUsers) 
// [ { id: 1, name: 'Jack' }, { id: 2, name: 'Lucy' },{ id: 3, name: 'Tom' } ]
console.log(users) 
// [ { id: 1, name: 'Jack' }, { id: 2, name: 'Lucy' },{ id: 3, name: 'Tom' } ]

/* 倒序 */
console.log(users.sort((x, y) => y.id - x.id)) 
// [ { id: 3, name: 'Tom' },{ id: 2, name: 'Lucy' },{ id: 1, name: 'Jack' } ]

// sort 方法改变原数组

reverse()

颠倒数组中元素的顺序

js
const arr = [1,2,3,4,5]
console.log(arr.reverse()) // [ 5, 4, 3, 2, 1 ]
console.log(arr) // [ 5, 4, 3, 2, 1 ]

// 改变原数组

join() 和 split()

  • join 通过给定的分隔符将数组转换成字符串

  • split 通过给定的分隔符将字符串分割成一个数组。

js
const arr = ['h', 'e', 'l', 'l', 'o']
console.log(arr.join(';')) // h;e;l;l;o
console.log(arr.join('')) // hello

let str = 'hello, world'
console.log(str.split(',')) // [ 'hello', ' world' ]

flat(扁平化数组)

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

语法:const newArray = arr.flat([depth])

  • depth指定要提取嵌套数组的结构深度,默认值为 1。
  • depth使用 Infinity,可展开任意深度的嵌套数组
js
/* 扁平化嵌套数组 */
const arr1 = [1, 2, [3, 4]]
arr1.flat()
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]]
arr2.flat()
// [1, 2, 3, 4, [5, 6]]

const arr3 = [1, 2, [3, 4, [5, 6]]]
arr3.flat(2)
// [1, 2, 3, 4, 5, 6]

/* 展开任意深度的嵌套数组 */
const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]
arr4.flat(Infinity)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

去重

  • 方法一: 使用Map结构

js
const arr = [1, 2, 2, '1', 3, '1']
function unique(arr) {
  let map = new Map()
  for(let member of arr) {
    if (!map.has(member)) {
      map.set(member, 1)
    }
  }
  return Array.from(map.keys()) 
}
const res = unique(arr)
console.log(res) // [ 1, 2, '1', 3 ]
  • 方法二 : 使用filter()及indexOf()

js
function unique(arr) {
  const res = arr.filter((item, index, array) => {
    return array.indexOf(item) === index
  })
  return res
}
console.log(unique(arr)) // [ 1, 2, '1', 3 ]
  • 方法三: 使用 Set结构

js
function unique(arr) {
  return [...new Set(arr)]
}
console.log(unique(arr)) // [ 1, 2, '1', 3 ]

排序

  • 冒泡排序

思考:比较相邻的两个元素,如果前一个比后一个大,则交换位置。

js
const arr = [3, 2, 6, 5, 1, 0, 9, 10]

function sort(arr) {
  for (let i=0; i<arr.length - 1; i++) {
    for (let j=0; j<arr.length - i - 1; j++) {
      if (arr[j] > arr[j+1]) {
        let temp = arr[j]
        arr[j] = arr[j+1]
        arr[j+1] = temp
      }
    }
  }
  return arr
}
console.log(sort(arr)) // [ 0, 1, 2, 3, 5, 6, 9, 10 ]
  • 快速排序

思考: 第一次排序时将数据分成两部分,一部分比另一部分的所有数据都要小,然后递归调用,两部分数据都实行快排

js
function quickSort(arr) {
  if (arr.length <= 1) return arr
  let midIndex = Math.floor(arr.length/2)
  let midVal = arr.splice(midIndex, 1)[0]
  const smaller = []
  const bigger = []
  for (let ele of arr) {
    if (ele < midVal) {
      smaller.push(ele)
    } else {
      bigger.push(ele)
    }
  }
  return quickSort(smaller).concat(midVal, quickSort(bigger))
}

console.log(quickSort(arr)) // [ 0, 1, 2, 3, 5, 6, 9, 10 ]

扁平化

js
const arr = [2, 1, [5, 3, ['a', 'b', [8]]], [9]]

function flatten(arr) {
  return arr.flat(Infinity)
}

console.log(flatten(arr)) // [ 2, 1, 5, 3, 'a', 'b', 8, 9 ]
  • 循环,递归

js
function flatten(arr) {
  let res = []
  for (let ele of arr) {
    if (Array.isArray(ele)) {
      res = res.concat(flatten(ele))
    } else {
      res.push(ele)
    }
  }
  return res
}

console.log(flatten(arr)) // [ 2, 1, 5, 3, 'a', 'b', 8, 9 ]
js
function flatten(arr) {
  const res = arr.reduce((prev, cur) => {
    return prev.concat(
      Array.isArray(cur) ? flatten(cur) : cur
    )
  }, [])
  return res
}

console.log(flatten(arr)) // [ 2, 1, 5, 3, 'a', 'b', 8, 9 ]

Updated at: