🎓 作者简介: 前端领域优质创作者
🚪 资源导航: 传送门=>
🎬 个人主页: 江城开朗的豌豆
🌐 个人网站: 江城开朗的豌豆 🌍
📧 个人邮箱: YANG_TAO_WEB@163.com 📩
💬 个人微信: y_t_t_t_ 📱
📌 座 右 铭: 生活就像心电图,一帆风顺就证明你挂了。 💔
👥 QQ群: 906392632 (前端技术交流群) 💬
作为前端开发者最近在代码审查时发现了一个隐蔽的Bug:同事在无意中修改了原始数组,导致程序出现了难以追踪的问题。这让他意识到,清楚掌握哪些数组方法会改变原数组、哪些不会,是每个JavaScript开发者必备的技能。今天,我们就来彻底理清这个知识点。
会改变原数组的方法(破坏性方法)
1. 增删元素三剑客
const team = ['杨涛', '李四', '王五'];
// push - 尾部添加
team.push('赵六');
console.log(team); // ['杨涛', '李四', '王五', '赵六']
// pop - 尾部删除
team.pop();
console.log(team); // ['杨涛', '李四', '王五']
// shift - 头部删除
team.shift();
console.log(team); // ['李四', '王五']
// unshift - 头部添加
team.unshift('张三');
console.log(team); // ['张三', '李四', '王五']
提醒:"这四个方法都会直接修改原数组,并返回被添加/删除的元素。"
2. 排序与反转
const numbers = [3, 1, 4, 2];
// sort - 排序
numbers.sort();
console.log(numbers); // [1, 2, 3, 4]
// reverse - 反转
numbers.reverse();
console.log(numbers); // [4, 3, 2, 1]
3. 强大的splice
const members = ['杨涛', '李四', '王五', '赵六'];
// 删除并添加元素
const removed = members.splice(1, 2, '张三', '钱七');
console.log(removed); // ['李四', '王五']
console.log(members); // ['杨涛', '张三', '钱七', '赵六']
笔记:"splice堪称数组手术刀,可以任意位置删除、添加或替换元素。"
不会改变原数组的方法(非破坏性方法)
1. 查询类方法
const staff = ['杨涛', '李四', '王五'];
// concat - 连接数组
const newStaff = staff.concat(['赵六']);
console.log(staff); // ['杨涛', '李四', '王五'] (未改变)
console.log(newStaff); // ['杨涛', '李四', '王五', '赵六']
// slice - 截取子数组
const part = staff.slice(1, 3);
console.log(part); // ['李四', '王五']
console.log(staff); // ['杨涛', '李四', '王五'] (未改变)
2. 迭代类方法
const scores = [85, 92, 78];
// map - 映射新数组
const newScores = scores.map(score => score + 5);
console.log(scores); // [85, 92, 78] (未改变)
console.log(newScores); // [90, 97, 83]
// filter - 过滤数组
const highScores = scores.filter(score => score > 80);
console.log(highScores); // [85, 92]
console.log(scores); // [85, 92, 78] (未改变)
心得:"这些方法都会返回新数组,非常适合函数式编程。"
容易混淆的特殊情况
1. fill方法的双重性
const arr = [1, 2, 3, 4];
const newArr = arr.fill(0, 1, 3);
console.log(arr); // [1, 0, 0, 4] (原数组被修改)
console.log(newArr); // [1, 0, 0, 4] (返回的是修改后的原数组)
2. sort的比较函数
const users = [
{ name: '杨涛', age: 28 },
{ name: '李四', age: 25 }
];
// 会改变原数组
const sortedUsers = users.sort((a, b) => a.age - b.age);
console.log(users === sortedUsers); // true (是同一个数组)
最佳实践建议
通过项目实践,杨涛总结出以下经验:
- 明确意图:如果确实需要修改原数组,使用破坏性方法
- 保持不可变:多数情况下优先使用非破坏性方法
- 性能权衡:对大型数组,适当使用破坏性方法可减少内存占用
- 清晰注释:使用破坏性方法时添加注释说明
// 好的实践:使用扩展运算符创建新数组
const original = ['杨涛', '李四'];
const updated = [...original, '王五'];
// 需要修改原数组时的注释
// 注意:此操作会直接修改original数组
original.push('赵六');
现代JavaScript的不可变方案
ES6及后续版本提供了更多保持不可变性的方式:
// 使用Object.freeze防止意外修改
const frozenArray = Object.freeze(['杨涛', '李四']);
// frozenArray.push('王五'); // 报错
// 使用新的with方法(ES2023)
const newArray = frozenArray.with(1, '王五');
console.log(frozenArray); // ['杨涛', '李四'] (未改变)
console.log(newArray); // ['杨涛', '王五']
实用速查表
为方便记忆,整理了这张表格:
| 方法 | 是否修改原数组 | 返回值 |
|---|---|---|
| push/pop | ✅ | 添加/删除的元素 |
| shift/unshift | ✅ | 添加/删除的元素 |
| splice | ✅ | 被删除的元素数组 |
| sort/reverse | ✅ | 排序后的原数组 |
| fill | ✅ | 修改后的原数组 |
| concat | ❌ | 新数组 |
| slice | ❌ | 子数组 |
| map/filter | ❌ | 新数组 |
| reduce | ❌ | 累计结果 |
| with | ❌ | 修改后的新数组(ES2023) |
总结
"理解数组方法的破坏性就像掌握外科手术器械的区别,"在团队分享时说,"有些像手术刀会直接改变组织,有些像检查仪器则保持原样。"
记住这个原则:
- 会改变原数组:push/pop/shift/unshift/splice/sort/reverse/fill/copyWithin
- 不会改变原数组:concat/slice/map/filter/reduce/with/find/findIndex等
掌握这些区别后,你将能更自信地操作数组,避免意外的副作用,写出更健壮的JavaScript代码。