数组是有序的数据集合,是 JavaScript 中最常用的数据结构之一。
🎯 创建数组#
数组字面量#
// 最常用的方式const fruits = ['苹果', '香蕉', '橘子']
// 空数组const empty = []
// 混合类���(不推荐)const mixed = [1, 'hello', true, { name: '张三' }, [1, 2]]
// 稀疏数组(有空位)const sparse = [1, , , 4]console.log(sparse.length) // 4console.log(sparse[1]) // undefinedArray 构造函数#
// 单个数字参数:创建指定长度的空数组const arr1 = new Array(3)console.log(arr1.length) // 3console.log(arr1[0]) // undefined
// 多个参数:创建包含这些元素的数组const arr2 = new Array(1, 2, 3)console.log(arr2) // [1, 2, 3]
// 🔶 单参数时容易混淆,建议用 Array.ofconst arr3 = Array.of(3)console.log(arr3) // [3]Array.from#
// 从类数组创建const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 }const arr1 = Array.from(arrayLike)console.log(arr1) // ["a", "b", "c"]
// 从字符串创建const arr2 = Array.from('hello')console.log(arr2) // ["h", "e", "l", "l", "o"]
// 从 Set 创建const set = new Set([1, 2, 2, 3])const arr3 = Array.from(set)console.log(arr3) // [1, 2, 3]
// 带映射函数const arr4 = Array.from([1, 2, 3], (x) => x * 2)console.log(arr4) // [2, 4, 6]
// 生成序列const sequence = Array.from({ length: 5 }, (_, i) => i + 1)console.log(sequence) // [1, 2, 3, 4, 5]Array.fill 填充#
// 创建并填充const zeros = new Array(5).fill(0)console.log(zeros) // [0, 0, 0, 0, 0]
// 填充指定范围const arr = [1, 2, 3, 4, 5]arr.fill(0, 1, 3) // 从索引1到3(不含)console.log(arr) // [1, 0, 0, 4, 5]
// 🔶 fill 对引用类型是共享的const refs = new Array(3).fill([])refs[0].push(1)console.log(refs) // [[1], [1], [1]](三个是同一个数组!)
// 正确创建独立数组const independent = Array.from({ length: 3 }, () => [])independent[0].push(1)console.log(independent) // [[1], [], []]索引访问#
读取和修改#
const arr = ['a', 'b', 'c', 'd', 'e']
// 读取console.log(arr[0]) // "a"console.log(arr[2]) // "c"console.log(arr[-1]) // undefined(不支持负索引)
// 使用 at() 支持负索引(ES2022)console.log(arr.at(0)) // "a"console.log(arr.at(-1)) // "e"(最后一个)console.log(arr.at(-2)) // "d"
// 修改arr[1] = 'B'console.log(arr) // ["a", "B", "c", "d", "e"]
// 超出范围添加(会产生空位)arr[10] = 'x'console.log(arr.length) // 11console.log(arr[7]) // undefinedlength 属性#
const arr = [1, 2, 3, 4, 5]
// 获取长度console.log(arr.length) // 5
// 设置 length 可以截断或扩展数组arr.length = 3console.log(arr) // [1, 2, 3]
arr.length = 5console.log(arr) // [1, 2, 3, empty × 2]
// 清空数组arr.length = 0console.log(arr) // []添加和删除元素#
push 和 pop(尾部操作)#
const arr = [1, 2, 3]
// push:添加到末尾,返回新长度const len = arr.push(4, 5)console.log(arr) // [1, 2, 3, 4, 5]console.log(len) // 5
// pop:移除最后一个,返回移除的元素const last = arr.pop()console.log(arr) // [1, 2, 3, 4]console.log(last) // 5
// 空数组 pop 返回 undefinedconsole.log([].pop()) // undefinedunshift 和 shift(头部操作)#
const arr = [1, 2, 3]
// unshift:添加到开头,返回新长度arr.unshift(0)console.log(arr) // [0, 1, 2, 3]
// shift:移除第一个,返回移除的元素const first = arr.shift()console.log(arr) // [1, 2, 3]console.log(first) // 0
// 🔶 头部操作比尾部操作慢(需要移动其他元素)splice(任意位置操作)#
const arr = [1, 2, 3, 4, 5]
// splice(start, deleteCount, ...items)
// 删除元素const removed = arr.splice(2, 2) // 从索引2删除2个console.log(arr) // [1, 2, 5]console.log(removed) // [3, 4]
// 插入元素arr.splice(2, 0, 'a', 'b') // 在索引2插入,不删除console.log(arr) // [1, 2, "a", "b", 5]
// 替换元素arr.splice(2, 2, 3, 4) // 删除2个,插入2个console.log(arr) // [1, 2, 3, 4, 5]
// 负索引arr.splice(-1, 1) // 删除最后一个console.log(arr) // [1, 2, 3, 4]concat(合并数组)#
const arr1 = [1, 2]const arr2 = [3, 4]const arr3 = [5, 6]
// 不修改原数组,返回新数组const combined = arr1.concat(arr2, arr3)console.log(combined) // [1, 2, 3, 4, 5, 6]console.log(arr1) // [1, 2](原数组不变)
// 也可以传入单个值const extended = arr1.concat(3, 4, [5, 6])console.log(extended) // [1, 2, 3, 4, 5, 6]
// 使用展开运算符(更常用)const merged = [...arr1, ...arr2, ...arr3]查找元素#
indexOf 和 lastIndexOf#
const arr = [1, 2, 3, 2, 1]
// indexOf:从前往后找console.log(arr.indexOf(2)) // 1console.log(arr.indexOf(5)) // -1(未找到)
// 指定起始位置console.log(arr.indexOf(2, 2)) // 3
// lastIndexOf:从后往前找console.log(arr.lastIndexOf(2)) // 3
// 🔶 使用严格相等比较console.log([1, 2, 3].indexOf('2')) // -1console.log([NaN].indexOf(NaN)) // -1(NaN 不等于自身)includes#
const arr = [1, 2, 3, NaN]
console.log(arr.includes(2)) // trueconsole.log(arr.includes(5)) // false
// 可以找到 NaNconsole.log(arr.includes(NaN)) // true(indexOf 做不到)
// 指定起始位置console.log(arr.includes(1, 1)) // falsefind 和 findIndex#
const users = [ { id: 1, name: '张三' }, { id: 2, name: '李四' }, { id: 3, name: '王五' },]
// find:返回第一个满足条件的元素const user = users.find((u) => u.id === 2)console.log(user) // { id: 2, name: "李四" }
// 未找到返回 undefinedconst notFound = users.find((u) => u.id === 5)console.log(notFound) // undefined
// findIndex:返回第一个满足条件的索引const index = users.findIndex((u) => u.name === '王五')console.log(index) // 2
// findLast 和 findLastIndex(ES2023)const lastEven = [1, 2, 3, 4, 5].findLast((n) => n % 2 === 0)console.log(lastEven) // 4遍历方法#
forEach#
const arr = ['a', 'b', 'c']
arr.forEach((item, index, array) => { console.log(index, item)})// 0 "a"// 1 "b"// 2 "c"
// 🔶 forEach 不能中断arr.forEach((item) => { if (item === 'b') return // 只是跳过当前迭代 console.log(item)})// 输出 a 和 c
// 需要中断时使用 for...of 或 some/everymap(映射)#
const numbers = [1, 2, 3, 4, 5]
// 返回新数组const doubled = numbers.map((n) => n * 2)console.log(doubled) // [2, 4, 6, 8, 10]console.log(numbers) // [1, 2, 3, 4, 5](原数组不变)
// 提取对象属性const users = [ { name: '张三', age: 25 }, { name: '李四', age: 30 },]const names = users.map((u) => u.name)console.log(names) // ["张三", "李四"]
// 链式调用const result = numbers.map((n) => n * 2).map((n) => n + 1)console.log(result) // [3, 5, 7, 9, 11]filter(过滤)#
const numbers = [1, 2, 3, 4, 5, 6]
// 返回满足条件的元素const evens = numbers.filter((n) => n % 2 === 0)console.log(evens) // [2, 4, 6]
// 过滤对象数组const users = [ { name: '张三', age: 25, active: true }, { name: '李四', age: 30, active: false }, { name: '王五', age: 28, active: true },]
const activeUsers = users.filter((u) => u.active)console.log(activeUsers.length) // 2
// 移除假值const mixed = [0, 1, '', 'hello', null, undefined, false, true]const truthy = mixed.filter(Boolean)console.log(truthy) // [1, "hello", true]reduce(归约)#
const numbers = [1, 2, 3, 4, 5]
// 求和const sum = numbers.reduce((acc, cur) => acc + cur, 0)console.log(sum) // 15
// 求最大值const max = numbers.reduce((a, b) => (a > b ? a : b))console.log(max) // 5
// 数组转对象const users = [ { id: 1, name: '张三' }, { id: 2, name: '李四' },]const userMap = users.reduce((map, user) => { map[user.id] = user return map}, {})console.log(userMap) // { 1: {...}, 2: {...} }
// 扁平化数组const nested = [[1, 2], [3, 4], [5]]const flat = nested.reduce((acc, cur) => acc.concat(cur), [])console.log(flat) // [1, 2, 3, 4, 5]
// 分组const items = [ { type: 'fruit', name: '苹果' }, { type: 'vegetable', name: '白菜' }, { type: 'fruit', name: '香蕉' },]const grouped = items.reduce((groups, item) => { const key = item.type groups[key] = groups[key] || [] groups[key].push(item) return groups}, {})// { fruit: [...], vegetable: [...] }
// Object.groupBy(ES2024)const grouped2 = Object.groupBy(items, (item) => item.type)some 和 every#
const numbers = [1, 2, 3, 4, 5]
// some:至少有一个满足条件console.log(numbers.some((n) => n > 3)) // trueconsole.log(numbers.some((n) => n > 10)) // false
// every:所有都满足条件console.log(numbers.every((n) => n > 0)) // trueconsole.log(numbers.every((n) => n > 3)) // false
// 可以提前终止const arr = [1, 2, 3, 4, 5]arr.some((n) => { console.log(n) return n === 3 // 找到 3 后停止})// 输出 1, 2, 3排序#
sort#
// 默认按字符串排序const arr = [10, 2, 1, 21]arr.sort()console.log(arr) // [1, 10, 2, 21](字符串顺序)
// 数字排序需要比较函数arr.sort((a, b) => a - b) // 升序console.log(arr) // [1, 2, 10, 21]
arr.sort((a, b) => b - a) // 降序console.log(arr) // [21, 10, 2, 1]
// 对象排序const users = [ { name: '张三', age: 30 }, { name: '李四', age: 25 }, { name: '王五', age: 28 },]users.sort((a, b) => a.age - b.age)// 按年龄升序
// 字符串排序const names = ['张三', '李四', '王五']names.sort((a, b) => a.localeCompare(b, 'zh-CN'))
// 🔶 sort 会修改原数组toSorted(ES2023,不可变)#
const arr = [3, 1, 2]
const sorted = arr.toSorted((a, b) => a - b)console.log(sorted) // [1, 2, 3]console.log(arr) // [3, 1, 2](原数组不变)reverse#
const arr = [1, 2, 3, 4, 5]arr.reverse()console.log(arr) // [5, 4, 3, 2, 1]
// toReversed(ES2023,不可变)const arr2 = [1, 2, 3]const reversed = arr2.toReversed()console.log(reversed) // [3, 2, 1]console.log(arr2) // [1, 2, 3](原数组不变)数组转换#
slice(切片)#
const arr = [1, 2, 3, 4, 5]
// 提取子数组(不修改原数组)console.log(arr.slice(1, 4)) // [2, 3, 4]console.log(arr.slice(2)) // [3, 4, 5]console.log(arr.slice(-2)) // [4, 5]console.log(arr.slice(1, -1)) // [2, 3, 4]
// 浅拷贝数组const copy = arr.slice()flat(扁平化)#
const nested = [1, [2, [3, [4]]]]
console.log(nested.flat()) // [1, 2, [3, [4]]](默认深度1)console.log(nested.flat(2)) // [1, 2, 3, [4]]console.log(nested.flat(Infinity)) // [1, 2, 3, 4](完全扁平)
// flatMap = map + flat(1)const arr = [1, 2, 3]const result = arr.flatMap((x) => [x, x * 2])console.log(result) // [1, 2, 2, 4, 3, 6]join(转字符串)#
const arr = ['苹果', '香蕉', '橘子']
console.log(arr.join()) // "苹果,香蕉,橘子"console.log(arr.join('-')) // "苹果-香蕉-橘子"console.log(arr.join('')) // "苹果香蕉橘子"
// 与 split 配合const str = 'a-b-c'const parts = str.split('-')const rejoined = parts.join('_')console.log(rejoined) // "a_b_c"判断数组#
// Array.isArray(推荐)console.log(Array.isArray([])) // trueconsole.log(Array.isArray({})) // falseconsole.log(Array.isArray('hello')) // false
// instanceof(跨 iframe 可能失效)console.log([] instanceof Array) // true
// Object.prototype.toStringconsole.log(Object.prototype.toString.call([])) // "[object Array]"不可变数组方法(ES2023)#
const arr = [3, 1, 2]
// toSorted - 排序arr.toSorted((a, b) => a - b) // [1, 2, 3]
// toReversed - 反转arr.toReversed() // [2, 1, 3]
// toSpliced - 替换arr.toSpliced(1, 1, 'a', 'b') // [3, "a", "b", 2]
// with - 替换单个元素arr.with(1, 100) // [3, 100, 2]
// 原数组始终不变console.log(arr) // [3, 1, 2]实用技巧#
// 去重const unique = [...new Set([1, 2, 2, 3, 3, 3])]console.log(unique) // [1, 2, 3]
// 交集const intersection = [1, 2, 3].filter((x) => [2, 3, 4].includes(x))console.log(intersection) // [2, 3]
// 差集const difference = [1, 2, 3].filter((x) => ![2, 3, 4].includes(x))console.log(difference) // [1]
// 打乱数组function shuffle(arr) { return arr.sort(() => Math.random() - 0.5)}
// 随机取一个function randomItem(arr) { return arr[Math.floor(Math.random() * arr.length)]}
// 分块function chunk(arr, size) { return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) => arr.slice(i * size, (i + 1) * size) )}chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]总结#
| 方法 | 作用 | 修改原数组 | 返回值 |
|---|---|---|---|
| push/pop | 尾部操作 | 是 | 长度/元素 |
| unshift/shift | 头部操作 | 是 | 长度/元素 |
| splice | 任意位置操作 | 是 | 删除的元素 |
| map | 映射 | 否 | 新数组 |
| filter | 过滤 | 否 | 新数组 |
| reduce | 归约 | 否 | 任意值 |
| find | 查找元素 | 否 | 元素/undefined |
| sort | 排序 | 是 | 原数组 |
| toSorted | 排序 | 否 | 新数组 |
核心建议:
- 优先使用不修改原数组的方法
- 用
Array.isArray()判断数组 - 用
at(-1)获取最后一个元素 - 链式调用时注意返回值类型