数组
声明
创建一个空数组有两种语法:
let arr = new Array()
let arr = []
数组元素从 0 开始编号, 我们可以将元素的索引值填写在方括号内来获取元素:
const user = ['Tom', 'Jack', 'Rose']
console.log(user[0]) // Tom
console.log(user[1]) // Jack
console.log(user[2]) // Rose
length 属性的值是数组中元素的总个数
console.log(user.length) // 3
数组可以存储任何类型的元素。
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 (取出数组的最后一个元素)
const fruits = ['apple', 'orange', 'banana']
console.log(fruits.pop()) // banana
console.log(fruits) // [ 'apple', 'orange' ]
// pop 方法返回被删除的元素
- push (在数组末端添加元素)
const fruits = ['apple', 'orange', 'banana']
console.log(fruits.push('pear')) // 4
console.log(fruits) // [ 'apple', 'orange', 'banana', 'pear' ]
// push 方法返回数组的长度
作用于数组前端的方法
- shift (取出数组的第一个元素)
const fruits = ['apple', 'orange', 'banana']
console.log(fruits.shift()) // apple
console.log(fruits) // [ 'orange', 'banana' ]
// shift 方法返回被删除的元素
- unshift (在数组的前端添加元素)
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。最后返回已删除元素的数组。
/* 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 方法改变原数组
允许负向索引 (从数组末尾计算位置)
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 (不包括该元素)的原数组中的元素。
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...)
它接受任意数量的参数 — 数组或值
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 方法不改变原数组
reduce
对数组中的每个元素依次执行所提供的回调函数(升序执行)
语法: arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
callback 接收四个参数
- accumulator 上一次调用回调时返回的累积值, 或是initialValue的值
- currentValue 数组中正在处理的元素
- currentIndex 数组中正在处理的元素的索引(如果提供了initialValue,则起始索引号为0,否则为1)
- array 调用reduce的数组
initialValue
- 作为第一次调用 callback函数时的第一个参数的值。 如果不传,则使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
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。
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
const arr = [NaN]
console.log(arr.indexOf(NaN)) // -1 期望返回0
console.log(arr.includes(NaN)) // true
find()
语法:
arr.find(function(item, index, array){
// 如果查询到返回 true , 查询停止
})
该函数对数组中的每个元素重复调用
- item 是元素
- index 是索引
- array 是数组本身
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()
语法
arr.filter(function(item, index, array){})
语法与 find 大致相同,返回的是所有匹配元素组成的数组
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
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()
语法
arr.map(function(item, index, array){
// 返回新值而不是当前元素
})
它对数组中每个元素调用函数并返回符合结果的数组
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()
语法
function compare(a, b){
if (a > b) return 1
if (a == b) return 0
if (a < b) return -1
}
arr.sort(compare)
接收一个比较函数来确认排序方式, 正数表示更大,而负数表示更小
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()
颠倒数组中元素的顺序
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()
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,可展开任意深度的嵌套数组
/* 扁平化嵌套数组 */
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结构
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()
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结构
function unique(arr) {
return [...new Set(arr)]
}
console.log(unique(arr)) // [ 1, 2, '1', 3 ]
排序
冒泡排序
思考:比较相邻的两个元素,如果前一个比后一个大,则交换位置。
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 ]
快速排序
思考: 第一次排序时将数据分成两部分,一部分比另一部分的所有数据都要小,然后递归调用,两部分数据都实行快排
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 ]
扁平化
flat()
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 ]
循环,递归
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 ]
reduce()
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 ]