一、创建数组的几种方式

在 JavaScript 中,创建数组有多种方式,每种方式都有自己的适用场景:

// 1. 字面量方式(推荐,简洁直观)
const fruits = ['苹果', '香蕉', '橙子']

// 2. 构造函数方式
const numbers = new Array(1, 2, 3)

// 3. Array.of(解决 new Array 的怪异行为)
const single = Array.of(5) // [5]
const weird = new Array(5) // [empty × 5]

// 4. Array.from(从类数组或可迭代对象创建)
const chars = Array.from('小明') // ['小', '明']

建议:日常开发中,优先使用字面量方式,简洁且不易出错。Array.ofArray.from 适合特殊场景,比如处理类数组或明确需要单个元素数组时。

二、遍历数组的正确姿势

遍历数组是开发中最常见的需求之一,JavaScript 提供了多种方法,适合不同场景。我们来逐一分析。

1. 基础遍历方法

for 循环

传统且灵活,可以精确控制遍历过程:

const fruits = ['苹果', '香蕉', '橙子']
for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i])
}

优点:支持 breakcontinue,适合需要中断或跳过循环的场景。
缺点:代码稍显冗长。

for...of 循环

ES6 引入,语法简洁,专注于元素本身:

for (const fruit of fruits) {
  console.log(fruit)
}

优点:简洁直观,适合简单遍历。
缺点:无法直接访问索引(可以通过 entries() 获取)。

forEach 方法

数组内置方法,适合快速遍历:

fruits.forEach((fruit, index) => {
  console.log(`${index} 个水果是: ${fruit}`)
})

优点:函数式风格,代码简洁。
缺点:无法使用 breakreturn 中断。

2. 功能性遍历方法

map 方法

将数组转换为新数组,常用于数据转换:

const numbers = [1, 2, 3, 4]
const doubled = numbers.map(num => num * 2)
// 结果: [2, 4, 6, 8]

场景:需要生成新数组,比如计算折扣价格、格式化数据。

filter 方法

筛选符合条件的元素:

const scores = [75, 90, 45, 60, 88]
const passingScores = scores.filter(score => score >= 60)
// 结果: [75, 90, 60, 88]

场景:过滤数据,比如显示通过考试的学生、筛选价格低于某值的商品。

reduce 方法

将数组归约为单个值,功能强大:

const numbers = [1, 2, 3, 4, 5]
const sum = numbers.reduce((total, current) => total + current, 0)
// 结果: 15

场景:求和、统计、复杂数据聚合。

3. 查找类方法

find 和 findIndex

快速找到符合条件的元素或其索引:

const users = [
  { name: '张三', age: 28 },
  { name: '李四', age: 17 },
  { name: '王五', age: 32 }
]
const adult = users.find(user => user.age >= 18) // { name: '张三', age: 28 }
const adultIndex = users.findIndex(user => user.age >= 18) // 0

场景:查找特定用户、商品等。

some 和 every

检查数组是否满足某些条件:

const ages = [18, 21, 28, 16]
const hasMinor = ages.some(age => age < 18) // true
const allAdults = ages.every(age => age >= 18) // false

场景:验证数据,比如检查是否所有人成年、是否至少有一个低价商品。

4. 其他遍历方法

for...in 循环

主要为对象设计,不推荐用于数组:

for (const index in fruits) {
  console.log(fruits[index])
}

问题:可能遍历到继承的属性,性能较差。

while 和 do-while

适合不确定循环次数的场景:

let i = 0
while (i < fruits.length) {
  console.log(fruits[i])
  i++
}

场景:动态条件控制,比如处理用户输入。

5. 选择建议

  • 简单遍历:用 for...offorEach,代码简洁。
  • 需要中断:用 for 循环,支持 break
  • 数据转换:用 mapfilterreduce
  • 性能敏感for 循环通常最快,但差距不大,除非处理超大数组。

三、添加和删除元素

数组的增删操作很常见,以下是常用方法:

const team = ['张三', '李四']

// 末尾操作
team.push('王五') // 添加,返回新长度 3
const last = team.pop() // 删除,返回 '王五'

// 开头操作
team.unshift('赵六') // 添加,返回新长度 3
const first = team.shift() // 删除,返回 '赵六'

// 任意位置操作
team.splice(1, 0, '钱七') // 插入 '钱七'
team.splice(0, 1) // 删除索引 0 的元素

注意splice 会修改原数组,谨慎使用。

四、数组转换与处理

1. 连接与分割

const colors = ['红', '绿', '蓝']
const colorString = colors.join('和') // '红和绿和蓝'
const primaryColors = colors.slice(0, 2) // ['红', '绿']
const moreColors = colors.concat(['黄', '紫']) // ['红', '绿', '蓝', '黄', '紫']

2. 扁平化数组

处理嵌套数组时,flat 是最简单的方法:

const nested = [1, [2, [3, 4]]]
const flattened = nested.flat(Infinity) // [1, 2, 3, 4]

手动实现扁平化(递归方式):

function flatten(arr) {
  return arr.reduce((acc, val) => {
    return acc.concat(Array.isArray(val) ? flatten(val) : val)
  }, [])
}

性能提示:内置 flat() 通常最快,递归方法适合自定义需求。

3. 排序与查找

const fruits = ['香蕉', '苹果', '橙子']
fruits.sort() // ['橙子', '苹果', '香蕉']
fruits.reverse() // ['香蕉', '葡萄', '苹果']

const scores = [40, 100, 1, 5]
scores.sort((a, b) => a - b) // [1, 5, 40, 100]

const appleIndex = fruits.indexOf('苹果') // 2
const hasOrange = fruits.includes('橙子') // true

现代方法(ES2022+):

const numbers = [3, 1, 4, 2]
const sorted = numbers.toSorted() // [1, 2, 3, 4],不改原数组
const reversed = numbers.toReversed() // [2, 4, 1, 3]
const modified = numbers.with(0, 9) // [9, 1, 4, 2]

五、去重操作

数组去重是常见需求,Set 是最简洁高效的方法:

const arr = [1, 2, 2, 3, 3]
const unique = [...new Set(arr)] // [1, 2, 3]

对于对象数组,按特定键去重:

const objArr = [
  { id: 1, name: '张三' },
  { id: 2, name: '李四' },
  { id: 1, name: '张三' }
]
const uniqueObjArr = Array.from(
  new Map(objArr.map(item => [item.id, item])).values()
)
// 结果: [{ id: 1, name: '张三' }, { id: 2, name: '李四' }]

性能提示Set 方法对简单类型最快,Map 适合对象数组。

六、类数组的处理

类数组(如 arguments、NodeList)有 length 和索引,但无数组方法。转换方法:

function example() {
  const args = Array.from(arguments) // 或 [...arguments]
  return args.map(x => x * 2)
}
console.log(example(1, 2, 3)) // [2, 4, 6]

场景:处理 DOM 元素集合、函数参数等。

七、总结

数组操作是 JavaScript 开发的基石。从基础的 for 循环到现代的 toSortedflat,每种方法都有其独特价值。记住以下几点:

  • 简单优先:用 for...offorEach 处理简单遍历。
  • 函数式编程mapfilterreduce 让代码更优雅。
  • 性能优化:大数组用 Set 去重,for 循环遍历。
  • 现代特性:ES2022+ 的方法(如 toSorted)更安全,优先考虑。