← JavaScript数组 JavaScript字符串 →

JavaScript数组方法

原创 2026-03-13 JavaScript 已有人查阅

JavaScript数组方法手册:从基础操作到ES2025新特性

什么是数组方法?

数组方法是JavaScript内置的一系列函数,它们让我们能够高效地操作数组中的数据。无论是添加、删除、查找、排序还是遍历元素,数组方法都能帮我们用简洁的代码完成复杂的数据处理任务。

// 一个简单的数组
let students = ['张三', '李四', '王五', '赵六'];

// 使用数组方法快速操作
console.log(students.join(' → '));  // 张三 → 李四 → 王五 → 赵六
students.push('钱七');               // 添加元素
console.log(students);               // ['张三', '李四', '王五', '赵六', '钱七']

基础操作方法

1. toString() - 数组转字符串

将数组转换为逗号分隔的字符串,不会修改原数组。

let colors = ['红色', '蓝色', '绿色', '黄颜色'];
console.log(colors.toString());  // '红色,蓝色,绿色,黄颜色'

// 实际应用:快速打印数组内容
let scores = [85, 92, 78, 90];
console.log('成绩:' + scores.toString());  // 成绩:85,92,78,90

2. at() - 通过索引访问元素(ES2022)

使用正数或负数索引访问数组元素,负数从末尾开始计数。

let fruits = ['苹果', '香蕉', '橙子', '葡萄', '西瓜'];

// 正数索引(从0开始)
console.log(fruits.at(0));   // 苹果
console.log(fruits.at(2));   // 橙子

// 负数索引(从-1开始)
console.log(fruits.at(-1));  // 西瓜(之后一个)
console.log(fruits.at(-2));  // 葡萄(倒数第二个)

// 传统写法对比
console.log(fruits[fruits.length - 1]);  // 西瓜(传统方式)

个人经验:at()方法让访问数组末尾元素变得非常优雅,特别是在不知道数组长度的情况下。如果你需要兼容旧环境,可以用方括号语法作为替代。

// 兼容性写法
function safeAt(array, index) {
    if (index >= 0) return array[index];
    return array[array.length + index];
}

3. forEach() - 遍历数组

对数组的每个元素执行一次提供的函数。

let users = [
    { name: '张三', age: 25 },
    { name: '李四', age: 30 },
    { name: '王五', age: 28 }
];

// 基本遍历
users.forEach(function(user) {
    console.log(user.name + '今年' + user.age + '岁');
});

// 使用箭头函数,并访问索引
users.forEach((user, index) => {
    console.log(`第${index + 1}位:${user.name}`);
});

// 计算年龄总和
let totalAge = 0;
users.forEach(user => {
    totalAge += user.age;
});
console.log('平均年龄:', totalAge / users.length);

4. join() - 用指定分隔符合并元素

将数组所有元素连接成一个字符串,可以指定分隔符。

let words = ['JavaScript', '数组', '方法', '教程'];

// 默认逗号分隔
console.log(words.join());        // 'JavaScript,数组,方法,教程'

// 自定义分隔符
console.log(words.join(' '));     // 'JavaScript 数组 方法 教程'
console.log(words.join('-'));     // 'JavaScript-数组-方法-教程'
console.log(words.join(''));      // 'JavaScript数组方法教程'

// 实际应用:构建URL参数
let params = ['page=1', 'limit=10', 'keyword=JavaScript'];
let queryString = params.join('&');
console.log(queryString);  // 'page=1&limit=10&keyword=JavaScript'

5. pop() - 删除之后一个元素

删除数组的之后一个元素,并返回该元素。

let tasks = ['任务1', '任务2', '任务3', '任务4'];
console.log('当前任务:', tasks);

// 完成之后一个任务
let completedTask = tasks.pop();
console.log('已完成:', completedTask);  // 任务4
console.log('剩余任务:', tasks);        // ['任务1', '任务2', '任务3']

// pop() 空数组返回 undefined
let empty = [];
console.log(empty.pop());  // undefined

6. push() - 在末尾添加元素

向数组末尾添加一个或多个元素,返回新数组的长度。

let cart = ['苹果', '香蕉'];
console.log('购物车:', cart);

// 添加单个商品
let newLength = cart.push('橙子');
console.log('新长度:', newLength);  // 3
console.log('购物车:', cart);       // ['苹果', '香蕉', '橙子']

// 添加多个商品
cart.push('葡萄', '西瓜');
console.log('购物车:', cart);       // ['苹果', '香蕉', '橙子', '葡萄', '西瓜']

// 实际应用:合并数组
let newItems = ['芒果', '草莓'];
cart.push(...newItems);  // 使用展开运算符
console.log('购物车:', cart);  // 添加了芒果和草莓

7. shift() - 删除第一个元素

删除数组的第一个元素,并返回该元素,所有剩余元素前移。

let queue = ['顾客1', '顾客2', '顾客3', '顾客4'];
console.log('排队队列:', queue);

// 服务第一位顾客
let served = queue.shift();
console.log('正在服务:', served);  // 顾客1
console.log('剩余队列:', queue);   // ['顾客2', '顾客3', '顾客4']

// shift() 空数组返回 undefined
let empty = [];
console.log(empty.shift());  // undefined

8. unshift() - 在开头添加元素

向数组开头添加一个或多个元素,返回新数组的长度。

let priorityTasks = ['任务3', '任务4'];
console.log('初始任务:', priorityTasks);

// 添加高优先级任务
let newLength = priorityTasks.unshift('任务1', '任务2');
console.log('新长度:', newLength);  // 4
console.log('更新后:', priorityTasks);  // ['任务1', '任务2', '任务3', '任务4']

// 实际应用:消息队列优先级
let messages = ['普通消息1', '普通消息2'];
messages.unshift('紧急消息');
console.log(messages);  // ['紧急消息', '普通消息1', '普通消息2']

9. concat() - 合并数组

合并两个或多个数组,返回新数组,不修改原数组。

let group1 = ['张三', '李四'];
let group2 = ['王五', '赵六'];
let group3 = ['钱七', '孙八'];

// 合并两个数组
let allStudents = group1.concat(group2);
console.log(allStudents);  // ['张三', '李四', '王五', '赵六']

// 合并多个数组
let everyone = group1.concat(group2, group3);
console.log(everyone);  // ['张三', '李四', '王五', '赵六', '钱七', '孙八']

// 合并数组和单个值
let combined = group1.concat('新同学', group2);
console.log(combined);  // ['张三', '李四', '新同学', '王五', '赵六']

// 现在写法(展开运算符)
let modernCombine = [...group1, ...group2];
console.log(modernCombine);  // ['张三', '李四', '王五', '赵六']

10. copyWithin() - 数组内复制

在数组内部将指定位置的元素复制到他位置,返回修改后的原数组。

let numbers = [1, 2, 3, 4, 5, 6];

// 将索引2开始的元素复制到索引0
numbers.copyWithin(0, 2);
console.log(numbers);  // [3, 4, 5, 4, 5, 6]

let letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];

// 将索引3到5的元素复制到索引1
letters.copyWithin(1, 3, 5);
console.log(letters);  // ['A', 'D', 'E', 'D', 'E', 'F', 'G']

// 使用负数索引
let animals = ['cat', 'dog', 'fish', 'bird', 'rabbit'];
animals.copyWithin(-3, -2);
console.log(animals);  // ['cat', 'dog', 'bird', 'rabbit', 'rabbit']

11. flat() - 扁平化数组

将嵌套数组"拉平"成为一维数组,可以指定扁平化深度。

let nestedArray = [1, [2, 3], [4, [5, 6]]];

// 默认扁平化一层
let flatOnce = nestedArray.flat();
console.log(flatOnce);  // [1, 2, 3, 4, [5, 6]]

// 指定扁平化深度
let flatTwice = nestedArray.flat(2);
console.log(flatTwice);  // [1, 2, 3, 4, 5, 6]

// 实际应用:处理多层嵌套数据
let data = [
    { category: '电子产品', items: ['手机', '电脑', '平板'] },
    { category: '服装', items: ['T恤', '裤子', '外套'] },
    { category: '食品', items: ['水果', '零食', '饮料'] }
];

let allItems = data.map(item => item.items).flat();
console.log(allItems);  // ['手机', '电脑', '平板', 'T恤', '裤子', '外套', '水果', '零食', '饮料']

// 更简洁的写法:flatMap()
let allItems2 = data.flatMap(item => item.items);
console.log(allItems2);  // 同上

12. splice() - 多功能操作

可以删除、插入、替换数组元素,直接修改原数组。

let months = ['一月', '二月', '三月', '四月', '五月', '六月'];
console.log('原始月份:', months);

// 1. 删除元素
let removed = months.splice(2, 2);  // 从索引2删除2个元素
console.log('删除的元素:', removed);  // ['三月', '四月']
console.log('删除后:', months);       // ['一月', '二月', '五月', '六月']

// 2. 插入元素
months.splice(2, 0, '三月', '四月');  // 在索引2插入,不删除
console.log('插入后:', months);       // ['一月', '二月', '三月', '四月', '五月', '六月']

// 3. 替换元素
months.splice(3, 1, '四月(修订)');
console.log('替换后:', months);  // ['一月', '二月', '三月', '四月(修订)', '五月', '六月']

// 4. 综合操作:删除2个,插入3个
let colors = ['红', '绿', '蓝', '黄', '紫'];
colors.splice(1, 2, '橙', '青', '粉');
console.log(colors);  // ['红', '橙', '青', '粉', '黄', '紫']

13. toSpliced() - 安全版splice(ES2025)

与splice功能相同,但返回新数组,不修改原数组。

let months = ['一月', '二月', '三月', '四月', '五月', '六月'];

// 创建修改后的新数组,原数组不变
let newMonths = months.toSpliced(2, 2, '三月(新)', '四月(新)');

console.log('原数组:', months);     // ['一月', '二月', '三月', '四月', '五月', '六月']
console.log('新数组:', newMonths);  // ['一月', '二月', '三月(新)', '四月(新)', '五月', '六月']

// 链式调用
let result = months
    .toSpliced(1, 1, '二月(新)')
    .toSpliced(3, 1, '五月(新)');

console.log(result);

个人见解:toSpliced()这类不修改原数组的方法在函数式编程中非常有用,特别是当我们需要保持数据不可变时。在React/Vue这类框架中,这种模式能避免意外的副作用。

14. slice() - 提取子数组

返回数组的一段切片,不修改原数组。

let fruits = ['苹果', '香蕉', '橙子', '葡萄', '西瓜', '芒果'];

// 提取从索引1到索引4(不包括4)
let slice1 = fruits.slice(1, 4);
console.log(slice1);  // ['香蕉', '橙子', '葡萄']

// 只指定起始位置
let slice2 = fruits.slice(2);
console.log(slice2);  // ['橙子', '葡萄', '西瓜', '芒果']

// 使用负数索引
let slice3 = fruits.slice(-3);
console.log(slice3);  // ['葡萄', '西瓜', '芒果']

let slice4 = fruits.slice(1, -2);
console.log(slice4);  // ['香蕉', '橙子', '葡萄']

// 复制整个数组
let copy = fruits.slice();
console.log(copy);  // 数组副本

查找和检测方法

1. indexOf() - 查找元素索引

返回指定元素在数组中首次出现的索引,找不到返回-1。

let colors = ['红色', '蓝色', '绿色', '蓝色', '黄颜色'];

console.log(colors.indexOf('蓝色'));   // 1(第一次出现)
console.log(colors.indexOf('紫色'));   // -1(不存在)

// 指定开始搜索的位置
console.log(colors.indexOf('蓝色', 2));  // 3(从索引2开始搜索)

// 实际应用:检查元素是否存在
function contains(array, element) {
    return array.indexOf(element) !== -1;
}

console.log(contains(colors, '绿色'));  // true
console.log(contains(colors, '黑色'));  // false

2. lastIndexOf() - 从后查找索引

返回指定元素在数组中之后一次出现的索引。

let colors = ['红色', '蓝色', '绿色', '蓝色', '黄颜色'];

console.log(colors.lastIndexOf('蓝色'));  // 3(之后一次出现)
console.log(colors.lastIndexOf('紫色'));  // -1

// 指定结束搜索的位置
console.log(colors.lastIndexOf('蓝色', 2));  // 1(从后往前搜索到索引2)

3. includes() - 是否包含元素

判断数组是否包含指定元素,返回布尔值。

let products = ['手机', '电脑', '平板', '耳机'];

console.log(products.includes('电脑'));   // true
console.log(products.includes('相机'));   // false

// 指定起始搜索位置
console.log(products.includes('耳机', 2));  // true(从索引2开始)
console.log(products.includes('手机', 1));  // false(从索引1开始,找不到手机)

// includes 能正确识别 NaN
let numbers = [1, 2, NaN, 4];
console.log(numbers.includes(NaN));  // true
console.log(numbers.indexOf(NaN));    // -1(indexOf不能识别NaN)

4. find() - 查找符合条件的元素

返回第一个满足测试函数的元素值,找不到返回undefined。

let users = [
    { id: 1, name: '张三', age: 25 },
    { id: 2, name: '李四', age: 30 },
    { id: 3, name: '王五', age: 28 },
    { id: 4, name: '赵六', age: 35 }
];

// 查找年龄大于30的第一个用户
let user = users.find(user => user.age > 30);
console.log(user);  // { id: 4, name: '赵六', age: 35 }

// 查找名字包含'王'的用户
let user2 = users.find(user => user.name.includes('王'));
console.log(user2);  // { id: 3, name: '王五', age: 28 }

// 查找不存在的条件
let user3 = users.find(user => user.age > 40);
console.log(user3);  // undefined

5. findIndex() - 查找符合条件的索引

返回第一个满足测试函数的元素索引,找不到返回-1。

let users = [
    { id: 1, name: '张三', age: 25 },
    { id: 2, name: '李四', age: 30 },
    { id: 3, name: '王五', age: 28 },
    { id: 4, name: '赵六', age: 35 }
];

// 查找年龄30岁的用户索引
let index = users.findIndex(user => user.age === 30);
console.log(index);  // 1

// 查找名字为'王五'的用户
let index2 = users.findIndex(user => user.name === '王五');
console.log(index2);  // 2

// 更新找到的用户
if (index2 !== -1) {
    users[index2].age = 29;
}
console.log(users[2]);  // { id: 3, name: '王五', age: 29 }

6. findLast() - 从后查找元素(ES2025)

从数组末尾开始查找,返回第一个满足条件的元素。

let users = [
    { id: 1, name: '张三', role: 'user' },
    { id: 2, name: '李四', role: 'admin' },
    { id: 3, name: '王五', role: 'user' },
    { id: 4, name: '赵六', role: 'admin' }
];

// 从后往前查找第一个管理员
let lastAdmin = users.findLast(user => user.role === 'admin');
console.log(lastAdmin);  // { id: 4, name: '赵六', role: 'admin' }

// 对比find()是从前往后
let firstAdmin = users.find(user => user.role === 'admin');
console.log(firstAdmin);  // { id: 2, name: '李四', role: 'admin' }

7. findLastIndex() - 从后查找索引(ES2025)

从数组末尾开始查找,返回第一个满足条件的元素索引。

let numbers = [5, 12, 8, 130, 44, 12, 9];

// 从后往前查找大于10的元素的索引
let lastIndex = numbers.findLastIndex(num => num > 10);
console.log(lastIndex);  // 5(值为12的元素)

// 查找等于12的之后一个位置
let lastTwelve = numbers.findLastIndex(num => num === 12);
console.log(lastTwelve);  // 5

// 不存在的条件
let notFound = numbers.findLastIndex(num => num > 200);
console.log(notFound);  // -1

排序和顺序方法

1. sort() - 排序数组

对数组元素进行排序,默认按字符串Unicode码点排序,直接修改原数组。

// 字符串排序
let fruits = ['香蕉', '苹果', '橙子', '葡萄', '草莓'];
fruits.sort();
console.log(fruits);  // ['苹果', '葡萄', '草莓', '香蕉', '橙子']

// 数字排序(需要提供比较函数)
let numbers = [45, 3, 27, 12, 8, 36, 19];

// 默认排序(转为字符串排序,结果不符合预期)
numbers.sort();
console.log('默认排序:', numbers);  // [12, 19, 27, 3, 36, 45, 8]

// 升序排序
numbers.sort((a, b) => a - b);
console.log('升序:', numbers);  // [3, 8, 12, 19, 27, 36, 45]

// 降序排序
numbers.sort((a, b) => b - a);
console.log('降序:', numbers);  // [45, 36, 27, 19, 12, 8, 3]

// 随机排序
let items = [1, 2, 3, 4, 5];
items.sort(() => Math.random() - 0.5);
console.log('随机顺序:', items);

2. reverse() - 反转数组

反转数组中元素的顺序,直接修改原数组。

let letters = ['A', 'B', 'C', 'D', 'E'];
console.log('原始数组:', letters);

letters.reverse();
console.log('反转后:', letters);  // ['E', 'D', 'C', 'B', 'A']

// 与sort结合实现降序排序
let scores = [85, 92, 78, 90, 88];
scores.sort((a, b) => a - b).reverse();
console.log('降序分数:', scores);  // [92, 90, 88, 85, 78]

// 更高效的降序:直接使用比较函数
scores.sort((a, b) => b - a);

3. toSorted() - 安全排序(ES2025)

与sort功能相同,但返回新数组,不修改原数组。

let numbers = [45, 3, 27, 12, 8, 36, 19];

// 创建排序后的新数组
let sortedAsc = numbers.toSorted((a, b) => a - b);
let sortedDesc = numbers.toSorted((a, b) => b - a);

console.log('原数组:', numbers);        // [45, 3, 27, 12, 8, 36, 19]
console.log('升序新数组:', sortedAsc);  // [3, 8, 12, 19, 27, 36, 45]
console.log('降序新数组:', sortedDesc); // [45, 36, 27, 19, 12, 8, 3]

// 不提供比较函数时,默认按字符串排序
let fruits = ['香蕉', '苹果', '橙子'];
let sortedFruits = fruits.toSorted();
console.log(sortedFruits);  // ['苹果', '香蕉', '橙子']

4. toReversed() - 安全反转(ES2025)

与reverse功能相同,但返回新数组,不修改原数组。

let letters = ['A', 'B', 'C', 'D', 'E'];

let reversed = letters.toReversed();

console.log('原数组:', letters);   // ['A', 'B', 'C', 'D', 'E']
console.log('反转后:', reversed);  // ['E', 'D', 'C', 'B', 'A']

// 链式调用
let result = [1, 2, 3, 4, 5]
    .toReversed()
    .map(x => x * 2);

console.log(result);  // [10, 8, 6, 4, 2]

5. 对象数组排序

使用自定义比较函数对对象数组进行排序。

let students = [
    { name: '张三', age: 25, score: 85 },
    { name: '李四', age: 22, score: 92 },
    { name: '王五', age: 28, score: 78 },
    { name: '赵六', age: 23, score: 96 }
];

// 按年龄升序
let byAge = [...students].sort((a, b) => a.age - b.age);
console.log('按年龄排序:', byAge);

// 按分数降序
let byScore = [...students].sort((a, b) => b.score - a.score);
console.log('按分数排序:', byScore);

// 按姓名排序(中文需要localeCompare)
let byName = [...students].sort((a, b) => a.name.localeCompare(b.name));
console.log('按姓名排序:', byName);

// 多级排序:先按年龄,再按分数
let multiSort = [...students].sort((a, b) => {
    if (a.age !== b.age) {
        return a.age - b.age;  // 先按年龄
    }
    return b.score - a.score;   // 年龄相同按分数
});
console.log('多级排序:', multiSort);

课程知识要点

  1. 基础操作:push/pop(末尾)、unshift/shift(开头)、splice(任意位置)

  2. 转换方法:toString()、join()、concat()、flat()

  3. 提取方法:slice()、copyWithin()、splice()(可删可加)

  4. 查找方法:indexOf()、lastIndexOf()、includes()、find()、findIndex()

  5. ES2022新特性:at()方法支持负数索引

  6. ES2025新特性:toSorted()、toReversed()、toSpliced()、findLast()、findLastIndex()

  7. 排序方法:sort()、reverse(),以及它们的不可变版本

  8. 遍历方法:forEach()用于迭代

  9. 对象数组处理:自定义比较函数进行复杂排序

开发实践建议

基于多年JavaScript开发经验,我对数组方法的使用建议如下:

// 1. 链式调用的威力
let products = [
    { name: '手机', price: 3299, category: '电子产品' },
    { name: 'T恤', price: 99, category: '服装' },
    { name: '电脑', price: 5999, category: '电子产品' },
    { name: '裤子', price: 199, category: '服装' }
];

let result = products
    .filter(p => p.category === '电子产品')
    .sort((a, b) => a.price - b.price)
    .map(p => p.name);

console.log(result);  // ['手机', '电脑']

// 2. 优先使用不可变方法(ES2025+)
let original = [1, 2, 3, 4, 5];
let modified = original
    .toReversed()
    .toSpliced(2, 1, 10)
    .toSorted((a, b) => a - b);

console.log('原数组不变:', original);
console.log('新数组:', modified);

// 3. 使用includes替代indexOf检查存在性
let items = ['apple', 'banana', 'orange'];
if (items.includes('banana')) {
    console.log('找到了!');  // 更直观
}

// 4. 使用at()访问末尾元素
let arr = [10, 20, 30, 40, 50];
console.log(arr.at(-1));  // 50(更优雅)
console.log(arr[arr.length - 1]);  // 50(传统方式)

// 5. 使用find/findIndex查找对象
let users = [{ id: 1, name: '张三' }, { id: 2, name: '李四' }];
let user = users.find(u => u.id === 2);
console.log(user);  // { id: 2, name: '李四' }

选择建议:

  • 需要修改原数组:使用push/pop/shift/unshift/splice/sort/reverse

  • 需要保持原数组不变:使用concat/slice/...展开运算符/toSorted/toReversed等

  • 需要查找元素:使用includes(存在性)、find(元素)、findIndex(索引)

  • 需要遍历:使用forEach(仅遍历)、map(转换)、filter(筛选)

  • 需要排序:使用sort(修改原数组)或toSorted(返回新数组)

掌握这些数组方法,能让你的JavaScript代码更加简洁、高效、易读。数组是日常开发中最常用的数据结构之一,熟练运用这些方法,是每个JavaScript开发者必备的技能。

← JavaScript数组 JavaScript字符串 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号