Skip to content

JavaScript数组方法分类详解2024:改变与不改变原数组方法完整指南

📊 SEO元描述:2024年最新JavaScript数组方法教程,详解push、pop、splice、slice、concat等方法分类。包含改变原数组和不改变原数组方法对比,适合前端开发者掌握JavaScript数组操作技巧。

核心关键词:JavaScript数组方法2024、数组方法分类、splice和slice区别、JavaScript数组操作、改变原数组方法

长尾关键词:JavaScript数组方法有哪些、splice和slice怎么区分、JavaScript数组方法分类、数组方法改变原数组吗


📚 JavaScript数组方法学习目标与核心收获

通过本节JavaScript数组方法分类详解,你将系统性掌握:

  • 改变原数组的方法:掌握push、pop、shift、unshift、splice、sort、reverse等方法
  • 不改变原数组的方法:学会slice、concat、join、indexOf、includes等方法
  • splice方法强大功能:深入理解splice的增删改查综合应用
  • slice vs splice对比:彻底区分这两个易混淆的方法
  • 数组方法选择策略:学会在不同场景下选择合适的数组方法
  • 数组方法最佳实践:掌握高效安全的数组操作技巧

🎯 适合人群

  • JavaScript进阶学习者的数组方法系统学习
  • 前端开发者的数组操作技能提升
  • 面试准备者的数组方法知识点复习
  • 代码重构者的数组操作优化参考

🌟 改变原数组的方法:直接修改数组结构

改变原数组的方法是什么?这些是会直接修改原数组内容、长度或顺序的方法。改变原数组的方法会在原数组上进行操作,不会创建新数组,这在内存效率和性能方面有重要意义。

改变原数组方法的核心特征

  • 🎯 直接修改:在原数组上进行操作,不创建新数组
  • 🔧 内存高效:不需要额外的内存空间
  • 💡 性能优越:避免了数组复制的开销
  • 📚 副作用明显:操作后原数组发生变化
  • 🚀 链式调用限制:大多数方法不支持链式调用

💡 学习建议:使用改变原数组的方法时要特别注意副作用,确保这是你想要的行为

push、pop、shift、unshift:数组两端操作

如何在数组两端添加和删除元素?

数组两端操作方法提供了在数组开头和结尾进行增删操作的功能:

javascript
// 🎉 数组两端操作方法详解
const fruits = ['banana', 'orange'];
console.log('初始数组:', fruits); // ['banana', 'orange']

// 1. push() - 在数组末尾添加一个或多个元素
const pushResult = fruits.push('apple');
console.log('push后:', fruits);      // ['banana', 'orange', 'apple']
console.log('push返回值:', pushResult); // 3 (新的数组长度)

// push可以添加多个元素
fruits.push('grape', 'kiwi');
console.log('push多个元素:', fruits); // ['banana', 'orange', 'apple', 'grape', 'kiwi']

// 2. pop() - 删除并返回数组最后一个元素
const popResult = fruits.pop();
console.log('pop后:', fruits);       // ['banana', 'orange', 'apple', 'grape']
console.log('pop返回值:', popResult);  // 'kiwi'

// 3. unshift() - 在数组开头添加一个或多个元素
const unshiftResult = fruits.unshift('mango');
console.log('unshift后:', fruits);      // ['mango', 'banana', 'orange', 'apple', 'grape']
console.log('unshift返回值:', unshiftResult); // 5 (新的数组长度)

// unshift可以添加多个元素
fruits.unshift('pear', 'peach');
console.log('unshift多个元素:', fruits); // ['pear', 'peach', 'mango', 'banana', 'orange', 'apple', 'grape']

// 4. shift() - 删除并返回数组第一个元素
const shiftResult = fruits.shift();
console.log('shift后:', fruits);       // ['peach', 'mango', 'banana', 'orange', 'apple', 'grape']
console.log('shift返回值:', shiftResult); // 'pear'

数组两端操作的性能特点

javascript
// 🔴 数组两端操作的性能分析
// push和pop操作 - O(1)时间复杂度
const testArray1 = [];
console.time('push操作');
for (let i = 0; i < 100000; i++) {
    testArray1.push(i);
}
console.timeEnd('push操作');

console.time('pop操作');
for (let i = 0; i < 100000; i++) {
    testArray1.pop();
}
console.timeEnd('pop操作');

// unshift和shift操作 - O(n)时间复杂度(需要移动所有元素)
const testArray2 = [];
console.time('unshift操作');
for (let i = 0; i < 10000; i++) { // 注意:减少了测试数量
    testArray2.unshift(i);
}
console.timeEnd('unshift操作');

console.time('shift操作');
for (let i = 0; i < 10000; i++) {
    testArray2.shift();
}
console.timeEnd('shift操作');

// 实际应用示例
class Stack {
    constructor() {
        this.items = [];
    }
    
    push(item) {
        return this.items.push(item);
    }
    
    pop() {
        return this.items.pop();
    }
    
    peek() {
        return this.items[this.items.length - 1];
    }
    
    isEmpty() {
        return this.items.length === 0;
    }
}

class Queue {
    constructor() {
        this.items = [];
    }
    
    enqueue(item) {
        return this.items.push(item);
    }
    
    dequeue() {
        return this.items.shift();
    }
    
    front() {
        return this.items[0];
    }
    
    isEmpty() {
        return this.items.length === 0;
    }
}

sort、reverse:数组排序和反转

javascript
// 🎉 数组排序和反转方法
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
const fruits = ['banana', 'apple', 'orange', 'grape'];

// 1. reverse() - 反转数组元素顺序
console.log('原数组:', numbers);        // [3, 1, 4, 1, 5, 9, 2, 6]
const reverseResult = numbers.reverse();
console.log('reverse后:', numbers);     // [6, 2, 9, 5, 1, 4, 1, 3]
console.log('reverse返回值:', reverseResult); // [6, 2, 9, 5, 1, 4, 1, 3] (返回原数组引用)

// 2. sort() - 排序数组元素
console.log('原数组:', fruits);         // ['banana', 'apple', 'orange', 'grape']
const sortResult = fruits.sort();
console.log('sort后:', fruits);         // ['apple', 'banana', 'grape', 'orange']
console.log('sort返回值:', sortResult);   // ['apple', 'banana', 'grape', 'orange'] (返回原数组引用)

// 🔴 sort()的默认行为 - 字符串排序
const nums = [10, 2, 1, 20, 3];
nums.sort();
console.log('数字默认排序:', nums);      // [1, 10, 2, 20, 3] ❌ 不是数字排序!

// 正确的数字排序
const numbers2 = [10, 2, 1, 20, 3];
numbers2.sort((a, b) => a - b);         // 升序
console.log('数字升序排序:', numbers2);  // [1, 2, 3, 10, 20]

const numbers3 = [10, 2, 1, 20, 3];
numbers3.sort((a, b) => b - a);         // 降序
console.log('数字降序排序:', numbers3);  // [20, 10, 3, 2, 1]

// 复杂对象排序
const students = [
    { name: 'Alice', age: 23, grade: 85 },
    { name: 'Bob', age: 21, grade: 92 },
    { name: 'Charlie', age: 22, grade: 78 }
];

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

// 按成绩排序
students.sort((a, b) => b.grade - a.grade);
console.log('按成绩排序:', students);

// 按姓名排序
students.sort((a, b) => a.name.localeCompare(b.name));
console.log('按姓名排序:', students);

🔴 splice方法:最强大的数组操作方法

splice方法的强大功能是什么?

splice方法是JavaScript中最强大的数组操作方法,可以同时进行删除、插入和替换操作:

javascript
// 🎉 splice方法的强大功能详解
// 语法:array.splice(start, deleteCount, item1, item2, ...)

const originalArray = ['a', 'b', 'c', 'd', 'e', 'f'];

// 1. 只删除元素
const arr1 = [...originalArray];
const deleted1 = arr1.splice(2, 2); // 从索引2开始删除2个元素
console.log('删除操作:', arr1);      // ['a', 'b', 'e', 'f']
console.log('被删除的元素:', deleted1); // ['c', 'd']

// 2. 只插入元素
const arr2 = [...originalArray];
const deleted2 = arr2.splice(2, 0, 'x', 'y'); // 在索引2插入'x', 'y'
console.log('插入操作:', arr2);      // ['a', 'b', 'x', 'y', 'c', 'd', 'e', 'f']
console.log('被删除的元素:', deleted2); // [] (没有删除)

// 3. 替换元素
const arr3 = [...originalArray];
const deleted3 = arr3.splice(2, 2, 'x', 'y', 'z'); // 删除2个,插入3个
console.log('替换操作:', arr3);      // ['a', 'b', 'x', 'y', 'z', 'e', 'f']
console.log('被删除的元素:', deleted3); // ['c', 'd']

// 4. 负数索引
const arr4 = [...originalArray];
const deleted4 = arr4.splice(-2, 1, 'x'); // 从倒数第2个位置删除1个,插入'x'
console.log('负数索引:', arr4);      // ['a', 'b', 'c', 'd', 'x', 'f']
console.log('被删除的元素:', deleted4); // ['e']

// 5. 删除到末尾
const arr5 = [...originalArray];
const deleted5 = arr5.splice(3); // 从索引3开始删除到末尾
console.log('删除到末尾:', arr5);    // ['a', 'b', 'c']
console.log('被删除的元素:', deleted5); // ['d', 'e', 'f']

splice方法的实际应用场景

javascript
// 🎉 splice方法的实际应用
// 场景1:数组元素的增删改查
class ArrayManager {
    constructor(initialArray = []) {
        this.data = [...initialArray];
    }
    
    // 在指定位置插入元素
    insert(index, ...items) {
        this.data.splice(index, 0, ...items);
        return this;
    }
    
    // 删除指定位置的元素
    remove(index, count = 1) {
        return this.data.splice(index, count);
    }
    
    // 替换指定位置的元素
    replace(index, count, ...newItems) {
        return this.data.splice(index, count, ...newItems);
    }
    
    // 移动元素位置
    move(fromIndex, toIndex) {
        const [item] = this.data.splice(fromIndex, 1);
        this.data.splice(toIndex, 0, item);
        return this;
    }
    
    // 获取数组副本
    toArray() {
        return [...this.data];
    }
}

// 使用示例
const manager = new ArrayManager(['a', 'b', 'c', 'd']);
console.log('初始:', manager.toArray()); // ['a', 'b', 'c', 'd']

manager.insert(2, 'x', 'y');
console.log('插入后:', manager.toArray()); // ['a', 'b', 'x', 'y', 'c', 'd']

manager.remove(1, 2);
console.log('删除后:', manager.toArray()); // ['a', 'y', 'c', 'd']

manager.replace(1, 1, 'z');
console.log('替换后:', manager.toArray()); // ['a', 'z', 'c', 'd']

manager.move(0, 2);
console.log('移动后:', manager.toArray()); // ['z', 'c', 'a', 'd']

// 场景2:数组去重(保持顺序)
function removeDuplicates(arr) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] === arr[j]) {
                arr.splice(j, 1);
                j--; // 调整索引
            }
        }
    }
    return arr;
}

const duplicateArray = [1, 2, 2, 3, 3, 3, 4];
removeDuplicates(duplicateArray);
console.log('去重后:', duplicateArray); // [1, 2, 3, 4]

🔧 不改变原数组的方法:创建新数组

不改变原数组的方法是什么?这些是不会修改原数组,而是返回新数组或其他值的方法。不改变原数组的方法遵循函数式编程的原则,避免副作用,提高代码的可预测性。

🔴 slice vs splice:易混淆对比

slice和splice有什么区别?这是面试常考点!

slice vs splice对比是JavaScript中最容易混淆的概念之一:

javascript
// 🔴 slice vs splice 详细对比
const originalArray = ['a', 'b', 'c', 'd', 'e'];

// === slice方法 ===
console.log('=== slice方法测试 ===');
const arr1 = [...originalArray];
const sliceResult = arr1.slice(1, 4); // 从索引1到4(不包括4)

console.log('原数组:', arr1);           // ['a', 'b', 'c', 'd', 'e'] ✅ 未改变
console.log('slice结果:', sliceResult);  // ['b', 'c', 'd'] ✅ 新数组
console.log('slice返回值类型:', typeof sliceResult); // object (数组)

// === splice方法 ===
console.log('=== splice方法测试 ===');
const arr2 = [...originalArray];
const spliceResult = arr2.splice(1, 3, 'x', 'y'); // 从索引1删除3个,插入'x', 'y'

console.log('原数组:', arr2);            // ['a', 'x', 'y', 'e'] ❌ 已改变
console.log('splice结果:', spliceResult); // ['b', 'c', 'd'] ✅ 被删除的元素
console.log('splice返回值类型:', typeof spliceResult); // object (数组)

// === 对比总结 ===
const comparisonTable = {
    '特性': ['slice', 'splice'],
    '是否改变原数组': ['否', '是'],
    '返回值': ['新数组(提取的元素)', '数组(被删除的元素)'],
    '主要用途': ['提取子数组', '增删改数组元素'],
    '参数': ['(start, end)', '(start, deleteCount, ...items)'],
    '性能': ['创建新数组,内存开销大', '直接修改,内存开销小']
};

console.table(comparisonTable);

slice和splice的实际应用对比

javascript
// 🎉 slice和splice的实际应用场景
// 场景1:数组分页 - 使用slice
function paginate(array, pageSize, pageNumber) {
    const start = (pageNumber - 1) * pageSize;
    const end = start + pageSize;
    return array.slice(start, end); // 不改变原数组
}

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log('第1页:', paginate(data, 3, 1)); // [1, 2, 3]
console.log('第2页:', paginate(data, 3, 2)); // [4, 5, 6]
console.log('原数组:', data);                // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 未改变

// 场景2:数组元素管理 - 使用splice
class TodoList {
    constructor() {
        this.todos = [];
    }
    
    add(todo) {
        this.todos.push(todo);
    }
    
    remove(index) {
        return this.todos.splice(index, 1)[0]; // 改变原数组
    }
    
    update(index, newTodo) {
        const [oldTodo] = this.todos.splice(index, 1, newTodo);
        return oldTodo;
    }
    
    getAll() {
        return this.todos.slice(); // 返回副本,不暴露内部数组
    }
}

const todoList = new TodoList();
todoList.add('学习JavaScript');
todoList.add('写代码');
todoList.add('休息');

console.log('所有任务:', todoList.getAll());
todoList.remove(1);
console.log('删除后:', todoList.getAll());

// 场景3:函数式编程 - 优先使用slice
function getFirstN(array, n) {
    return array.slice(0, n); // 函数式,无副作用
}

function getLastN(array, n) {
    return array.slice(-n); // 函数式,无副作用
}

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log('前3个:', getFirstN(numbers, 3)); // [1, 2, 3]
console.log('后3个:', getLastN(numbers, 3));  // [8, 9, 10]
console.log('原数组:', numbers);              // 保持不变

concat、join:数组连接和转换

javascript
// 🎉 concat和join方法详解
// 1. concat() - 连接数组
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [7, 8, 9];

// 连接多个数组
const concatenated = arr1.concat(arr2, arr3);
console.log('concat结果:', concatenated);    // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('原数组arr1:', arr1);           // [1, 2, 3] 未改变

// 连接数组和单个元素
const mixed = arr1.concat(4, [5, 6], 7);
console.log('混合连接:', mixed);             // [1, 2, 3, 4, 5, 6, 7]

// 现代替代方案:扩展运算符
const modernConcat = [...arr1, ...arr2, ...arr3];
console.log('扩展运算符:', modernConcat);    // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 2. join() - 数组转字符串
const fruits = ['apple', 'banana', 'orange'];

// 默认分隔符(逗号)
console.log('默认join:', fruits.join());     // 'apple,banana,orange'

// 自定义分隔符
console.log('空格分隔:', fruits.join(' '));   // 'apple banana orange'
console.log('横线分隔:', fruits.join(' - ')); // 'apple - banana - orange'
console.log('无分隔符:', fruits.join(''));    // 'applebananaorange'

// 实际应用:生成URL路径
const pathSegments = ['api', 'users', '123', 'profile'];
const url = '/' + pathSegments.join('/');
console.log('URL路径:', url);                // '/api/users/123/profile'

// 实际应用:生成SQL查询
const columns = ['id', 'name', 'email', 'created_at'];
const sql = `SELECT ${columns.join(', ')} FROM users`;
console.log('SQL查询:', sql);                // 'SELECT id, name, email, created_at FROM users'

indexOf、lastIndexOf、includes:数组查找

javascript
// 🎉 数组查找方法详解
const numbers = [1, 2, 3, 2, 4, 2, 5];
const fruits = ['apple', 'banana', 'orange', 'banana'];

// 1. indexOf() - 查找元素第一次出现的索引
console.log('indexOf(2):', numbers.indexOf(2));      // 1 (第一次出现)
console.log('indexOf(6):', numbers.indexOf(6));      // -1 (不存在)
console.log('indexOf(2, 3):', numbers.indexOf(2, 3)); // 3 (从索引3开始查找)

// 2. lastIndexOf() - 查找元素最后一次出现的索引
console.log('lastIndexOf(2):', numbers.lastIndexOf(2));    // 5 (最后一次出现)
console.log('lastIndexOf(6):', numbers.lastIndexOf(6));    // -1 (不存在)
console.log('lastIndexOf(2, 4):', numbers.lastIndexOf(2, 4)); // 3 (从索引4向前查找)

// 3. includes() - 检查数组是否包含某个元素 (ES2016)
console.log('includes(2):', numbers.includes(2));     // true
console.log('includes(6):', numbers.includes(6));     // false
console.log('includes(2, 3):', numbers.includes(2, 3)); // true (从索引3开始查找)

// 🔴 特殊值的查找
const specialValues = [1, NaN, null, undefined, 0, false, ''];

// NaN的特殊处理
console.log('indexOf(NaN):', specialValues.indexOf(NaN));     // -1 (indexOf无法找到NaN)
console.log('includes(NaN):', specialValues.includes(NaN));   // true (includes可以找到NaN)

// null和undefined
console.log('indexOf(null):', specialValues.indexOf(null));         // 2
console.log('indexOf(undefined):', specialValues.indexOf(undefined)); // 3
console.log('includes(null):', specialValues.includes(null));       // true
console.log('includes(undefined):', specialValues.includes(undefined)); // true

// 实际应用:数组去重
function removeDuplicatesWithIndexOf(arr) {
    const result = [];
    for (let i = 0; i < arr.length; i++) {
        if (result.indexOf(arr[i]) === -1) {
            result.push(arr[i]);
        }
    }
    return result;
}

function removeDuplicatesWithIncludes(arr) {
    const result = [];
    for (const item of arr) {
        if (!result.includes(item)) {
            result.push(item);
        }
    }
    return result;
}

const duplicates = [1, 2, 2, 3, 3, 3, 4, NaN, NaN];
console.log('indexOf去重:', removeDuplicatesWithIndexOf(duplicates));
console.log('includes去重:', removeDuplicatesWithIncludes(duplicates));

// 实际应用:权限检查
function hasPermission(userPermissions, requiredPermission) {
    return userPermissions.includes(requiredPermission);
}

const userPerms = ['read', 'write', 'delete'];
console.log('有写权限:', hasPermission(userPerms, 'write'));   // true
console.log('有管理权限:', hasPermission(userPerms, 'admin')); // false

📚 数组方法分类学习总结与下一步规划

✅ 本节核心收获回顾

通过本节JavaScript数组方法分类详解的学习,你已经掌握:

  1. 改变原数组的方法:理解push、pop、shift、unshift、splice、sort、reverse的用法
  2. 不改变原数组的方法:掌握slice、concat、join、indexOf、includes等方法
  3. splice方法强大功能:学会使用splice进行复杂的数组操作
  4. slice vs splice对比:彻底区分这两个易混淆的方法
  5. 数组方法选择策略:知道在不同场景下选择合适的方法

🎯 JavaScript数组下一步

  1. 学习数组高级操作:掌握数组去重、扁平化等实用技巧
  2. 深入高阶函数:学习map、filter、reduce等函数式编程方法
  3. 理解类数组对象:学习arguments、NodeList等的处理方法
  4. 掌握数组性能优化:了解不同方法的性能特点和最佳实践

💪 实践练习建议

  1. 实现数组工具库:封装常用的数组操作方法
  2. 方法性能对比:测试不同数组方法的性能差异
  3. 解决实际问题:使用数组方法解决具体的编程问题
  4. 代码重构练习:优化现有代码中的数组操作逻辑

"掌握数组方法的分类和特点是JavaScript编程的重要技能,为后续学习高阶函数和函数式编程打下基础!"