JavaScript 常用数组操作方法完整指南

📚 核心方法对比总览

方法 功能 修改原数组 返回值 时间复杂度 记忆技巧
slice() 切片/提取 ❌ 否 新数组/字符串 O(n) "切一片蛋糕" - 原蛋糕还在
splice() 剪接/增删改 ✅ 是 被删除元素数组 O(n) "剪辑电影" - 原片被修改
shift() 移除第一个 ✅ 是 移除的元素 O(n) "移除队首"
unshift() 前面添加 ✅ 是 新长度 O(n) "反方向shift"
pop() 移除最后一个 ✅ 是 移除的元素 O(1) "弹出栈顶"
push() 后面添加 ✅ 是 新长度 O(1) "推入栈"

一、🔪 slice() - 切片方法

1.1 基本语法

js 复制代码
// 数组和字符串都可用
array.slice(startIndex, endIndex)  // 包含start,不包含end
string.slice(startIndex, endIndex)

1.2 参数详解

js 复制代码
const arr = ['A', 'B', 'C', 'D', 'E'];

// 两个参数
arr.slice(1, 3);    // ['B', 'C']     (索引1到3,不含3)
arr.slice(0, -1);   // ['A', 'B', 'C', 'D']  (0到倒数第1个)

// 一个参数
arr.slice(2);       // ['C', 'D', 'E']  (从索引2到末尾)
arr.slice(-2);      // ['D', 'E']       (最后2个)

// 无参数 - 浅拷贝
const copy = arr.slice();  // 完整浅拷贝

1.3 实际应用

js 复制代码
// 1. 数组浅拷贝
const original = [1, 2, { name: 'John' }];
const copy = original.slice();
copy[0] = 999;           // 不影响原数组
copy[2].name = 'Jane';   // 影响原数组(对象引用相同)

// 2. 分页功能
function paginate(items, page = 1, pageSize = 10) {
  const start = (page - 1) * pageSize;
  return items.slice(start, start + pageSize);
}

// 3. 字符串操作
const url = 'https://api.example.com/users';
const domain = url.slice(8, url.indexOf('/', 8));  // 'api.example.com'

// 4. 获取数组部分
const getFirstN = (arr, n) => arr.slice(0, n);
const getLastN = (arr, n) => arr.slice(-n);

二、✂️ splice() - 剪接方法

2.1 基本语法

js 复制代码
array.splice(startIndex, deleteCount, item1, item2, ...)

2.2 三种主要用途

js 复制代码
const fruits = ['🍎', '🍌', '🍊', '🍇'];

// 1. 删除元素
const removed = fruits.splice(1, 2);  // 从索引1删除2个
// removed: ['🍌', '🍊']
// fruits: ['🍎', '🍇']

// 2. 插入元素
fruits.splice(1, 0, '🍉', '🍓');  // 在索引1插入,不删除
// fruits: ['🍎', '🍉', '🍓', '🍇']

// 3. 替换元素
fruits.splice(2, 1, '🍍');  // 替换索引2的元素
// fruits: ['🍎', '🍉', '🍍', '🍇']

2.3 实用技巧

js 复制代码
// 1. 删除最后一个元素(替代pop)
arr.splice(-1, 1);

// 2. 删除第一个元素(替代shift)
arr.splice(0, 1);

// 3. 清空数组
arr.splice(0, arr.length);

// 4. 在指定位置插入多个
const insertAt = (arr, index, ...items) => {
  arr.splice(index, 0, ...items);
  return arr;
};

三、🔄 shift() / unshift() - 队列前端操作

3.1 shift() - 移除第一个

js 复制代码
const queue = ['任务1', '任务2', '任务3'];

// 先进先出(FIFO)
const currentTask = queue.shift();
// currentTask: '任务1'
// queue: ['任务2', '任务3']

// 处理任务队列
while (queue.length > 0) {
  const task = queue.shift();
  processTask(task);
}

3.2 unshift() - 前面添加

js 复制代码
const history = ['页面2', '页面3'];

// 添加浏览历史(最新在最前)
history.unshift('页面1');
// history: ['页面1', '页面2', '页面3']

// 添加日志前缀
function addLogPrefix(logs, prefix) {
  logs.unshift(`[${prefix}]:`);
  return logs;
}

3.3 性能注意

js 复制代码
// ❌ shift/unshift 在大型数组上性能较差(需要移动所有元素)
const bigArray = new Array(1000000).fill('item');

// 如果要频繁操作数组前端,考虑使用双向队列
class Deque {
  constructor() {
    this.items = [];
  }
  
  addFront(item) {
    this.items.unshift(item);
  }
  
  removeFront() {
    return this.items.shift();
  }
}

四、📌 pop() / push() - 栈/队列后端操作

4.1 push() - 后面添加

js 复制代码
const stack = [];

// 入栈(LIFO)
stack.push('操作1');
stack.push('操作2');
stack.push('操作3');
// stack: ['操作1', '操作2', '操作3']

// 构建路径
const path = [];
path.push('users');
path.push('documents');
path.push('file.txt');
// path: ['users', 'documents', 'file.txt']

4.2 pop() - 移除最后一个

js 复制代码
// 出栈
const lastOperation = stack.pop();
// lastOperation: '操作3'
// stack: ['操作1', '操作2']

// 撤销操作实现
const undoStack = [];
const redoStack = [];

function doAction(action) {
  undoStack.push(action);
  execute(action);
}

function undo() {
  if (undoStack.length > 0) {
    const action = undoStack.pop();
    redoStack.push(action);
    revert(action);
  }
}

五、🎯 对比总结

5.1 读写特性对比

js 复制代码
const arr = [1, 2, 3, 4, 5];

// 读操作(不修改原数组)
const sliceResult = arr.slice(1, 4);  // [2, 3, 4]
console.log(arr);  // [1, 2, 3, 4, 5] ✅ 不变

// 写操作(修改原数组)
const spliceResult = arr.splice(1, 3);  // [2, 3, 4]
console.log(arr);  // [1, 5] ❌ 已修改

5.2 操作位置对比

text 复制代码
操作位置         前面          中间          后面
-------------------------------------------------
取出/删除     shift()      splice()      pop()
添加/插入     unshift()    splice()      push()
只读切片      slice()      slice()       slice()

5.3 返回值对比

js 复制代码
const arr = ['a', 'b', 'c', 'd'];

arr.slice(1, 3);   // 返回: ['b', 'c']   (新数组)
arr.splice(1, 2);  // 返回: ['b', 'c']   (删除的元素)
arr.shift();       // 返回: 'a'          (移除的元素)
arr.unshift('x');  // 返回: 4            (新长度)

六、🚀 最佳实践

6.1 选择指南

场景 推荐方法 原因
需要原数组不变 slice() 创建副本,不影响原始数据
需要修改原数组 splice() 原地操作,节省内存
队列操作(先进先出) push() + shift() 符合队列语义
栈操作(后进先出) push() + pop() 符合栈语义
数组前端操作频繁 考虑双向链表 shift/unshift性能差

6.2 性能优化

js 复制代码
// ❌ 避免在大数组上使用 shift/unshift
const largeArray = new Array(100000).fill('item');

// ✅ 如果需要频繁操作两端,使用链表或双端队列
class OptimizedArray {
  constructor() {
    this.items = [];
    this.frontIndex = 0;
  }
  
  shift() {
    if (this.frontIndex >= this.items.length) return undefined;
    const item = this.items[this.frontIndex];
    this.frontIndex++;
    // 定期清理前面的空位
    if (this.frontIndex > 1000) {
      this.items = this.items.slice(this.frontIndex);
      this.frontIndex = 0;
    }
    return item;
  }
}

6.3 现代JavaScript替代

js 复制代码
// slice 的现代替代
const arr = [1, 2, 3, 4, 5];
const copy1 = arr.slice();          // 传统
const copy2 = [...arr];             // ES6扩展运算符 ✅
const copy3 = Array.from(arr);      // ES6 Array.from

// push 的现代替代
arr.push(6, 7);                     // 传统
const newArr = [...arr, 6, 7];      // 扩展运算符(不修改原数组)

// unshift 的现代替代
arr.unshift(0);                     // 传统  
const newArr2 = [0, ...arr];        // 扩展运算符(不修改原数组)

七、📝 记忆口诀

7.1 按功能记忆

text 复制代码
读操作:slice     (只读不写)
写操作:splice    (增删改查)
队列头:shift/unshift
队列尾:pop/push

7.2 按名字记忆

  • slice → "切" → 切一片 → 只读
  • splice → "plice"(连接)→ 修改连接
  • shift → 移动 → 移除第一个
  • unshift → 反移动 → 前面添加
  • pop → "砰" → 弹出最后一个
  • push → 推 → 后面推入

7.3 一句话总结

slice切,splice改,shift/unshift动头,pop/push动尾


八、🔧 实用代码片段

js 复制代码
// 1. 安全的数组操作函数
const ArrayUtils = {
  // 安全的slice(处理负数、越界)
  safeSlice: (arr, start = 0, end = arr.length) => {
    const len = arr.length;
    start = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
    end = end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
    return arr.slice(start, end);
  },
  
  // 批量删除
  removeItems: (arr, indices) => {
    // 从大到小排序,避免索引变化
    indices.sort((a, b) => b - a);
    indices.forEach(index => {
      arr.splice(index, 1);
    });
    return arr;
  },
  
  // 插入到排序数组的合适位置
  insertSorted: (arr, item, compareFn = (a, b) => a - b) => {
    let low = 0, high = arr.length;
    while (low < high) {
      const mid = Math.floor((low + high) / 2);
      if (compareFn(arr[mid], item) < 0) {
        low = mid + 1;
      } else {
        high = mid;
      }
    }
    arr.splice(low, 0, item);
    return arr;
  }
};

九、📊 时间复杂度总结

操作 时间复杂度 说明
slice(start, end) O(k) k = end - start
splice(start, deleteCount, ...items) O(n) 需要移动元素
shift() O(n) 需要移动所有元素
unshift(...items) O(n + m) n=原长度, m=新增个数
pop() O(1) 直接移除最后一个
push(...items) O(m) m=新增个数

十、✅ 快速参考卡

js 复制代码
// 提取部分(不修改原数组)
const part = arr.slice(start, end);

// 删除/插入/替换(修改原数组)
const removed = arr.splice(start, deleteCount, ...items);

// 队列操作(先进先出)
queue.push(item);          // 入队
const item = queue.shift(); // 出队

// 栈操作(后进先出)
stack.push(item);          // 压栈
const item = stack.pop();   // 弹栈

// 数组前后操作
arr.unshift(...items);     // 前面添加
arr.shift();               // 移除第一个
arr.push(...items);        // 后面添加
arr.pop();                 // 移除最后一个

最后提醒

  • ✅ 需要保留原数组 时用 slice()
  • ✅ 需要修改原数组 时用 splice()
  • 队列操作push() + shift()
  • 栈操作push() + pop()
  • ❌ 避免在大数组上频繁使用 shift()/unshift()
相关推荐
一只小风华~3 小时前
Vue.js 核心知识点全面解析
前端·javascript·vue.js
2022.11.7始学前端3 小时前
n8n第七节 只提醒重要的待办
前端·javascript·ui·n8n
SakuraOnTheWay3 小时前
React Grab实践 | 记一次与Cursor的有趣对话
前端·cursor
阿星AI工作室4 小时前
gemini3手势互动圣诞树保姆级教程来了!附提示词
前端·人工智能
徐小夕4 小时前
知识库创业复盘:从闭源到开源,这3个教训价值百万
前端·javascript·github
xhxxx4 小时前
函数执行完就销毁?那闭包里的变量凭什么活下来!—— 深入 JS 内存模型
前端·javascript·ecmascript 6
StarkCoder4 小时前
求求你试试 DiffableDataSource!别再手算 indexPath 了(否则迟早崩)
前端
fxshy4 小时前
Cursor 前端Global Cursor Rules
前端·cursor
红彤彤4 小时前
前端接入sse(EventSource)(@fortaine/fetch-event-source)
前端
L、2184 小时前
统一日志与埋点系统:在 Flutter + OpenHarmony 混合架构中实现全链路可观测性
javascript·华为·智能手机·electron·harmonyos