📚 核心方法对比总览
| 方法 |
功能 |
修改原数组 |
返回值 |
时间复杂度 |
记忆技巧 |
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()