前言:数组到底有多重要?
先问个问题:你写的代码里,有多少时间是在跟数组打交道?我敢说至少30%!不管是处理接口数据、操作DOM列表,还是做各种计算,数组都是我们的好基友。
今天我就把数组那点事儿给你掰扯明白,从创建到遍历,一个不漏!
1. 创建数组:别只会用 [] 了!
1.1 ES6 Array.of():专治各种不服
先看个坑:
// 传统new Array的坑
new Array(3) // [empty × 3] 🤔
new Array(3, 4) // [3, 4] 😅
// Array.of的稳
Array.of(3) // [3] 👍
Array.of(3, 4) // [3, 4] 👍
Array.of() 就是个老实人,你传什么它就给你什么,从不耍花招。
1.2 ES6 Array.from():把"像数组的"变成真数组
有些东西看起来像数组,但用起来却不是那个味儿:
// 常见的类数组对象
const nodeList = document.querySelectorAll('div'); // NodeList
const argumentsObj = function() { return arguments; }(1, 2, 3); // Arguments
// 以前要这样转
const oldWay = Array.prototype.slice.call(nodeList);
// 现在爽多了
const newWay = Array.from(nodeList);
更厉害的是,它还能玩映射:
// 一键生成序列数组
Array.from({ length: 5 }, (_, index) => index * 2);
// [0, 2, 4, 6, 8] 爽不爽?
2. 方法大全:改变VS不改变,别搞混了!
2.1 改变原数组的"狠角色"(9个)
这些方法用的时候要小心,它们会直接修改原数组!
splice():数组的"瑞士军刀"
const fruits = ['苹果', '香蕉', '橙子'];
// 删除:从索引1开始删除1个元素
fruits.splice(1, 1); // 返回 ['香蕉'],fruits变成 ['苹果', '橙子']
// 添加:从索引1开始添加
fruits.splice(1, 0, '葡萄'); // fruits变成 ['苹果', '葡萄', '橙子']
// 替换:删除并添加
fruits.splice(1, 1, '芒果'); // fruits变成 ['苹果', '芒果', '橙子']
sort():排序的坑你踩过吗?
const numbers = [10, 2, 1, 20];
// 直接排序会按字符串比较!
numbers.sort(); // [1, 10, 2, 20] 😱
// 正确姿势
numbers.sort((a, b) => a - b); // [1, 2, 10, 20] 👍
pop() & shift():头尾删除兄弟
const arr = [1, 2, 3];
arr.pop(); // 返回3,arr变成[1, 2]
arr.shift(); // 返回1,arr变成[2]
push() & unshift():头尾添加兄弟
const arr = [2];
arr.push(3); // arr变成[2, 3],返回新长度2
arr.unshift(1); // arr变成[1, 2, 3],返回新长度3
reverse():翻转很简单
[1, 2, 3].reverse(); // [3, 2, 1]
ES6 copyWithin():内部拷贝
这个有点绕,但用对了很爽:
const arr = [1, 2, 3, 4, 5];
// 把索引3-4的元素复制到索引0的位置
arr.copyWithin(0, 3, 5); // [4, 5, 3, 4, 5]
ES6 fill():批量填充
// 创建长度为5,全是0的数组
const zeros = new Array(5).fill(0); // [0, 0, 0, 0, 0]
// 替换部分元素
[1, 2, 3, 4].fill('x', 1, 3); // [1, 'x', 'x', 4]
2.2 不改变原数组的"老实人"(8个)
这些方法会返回新数组或新值,原数组纹丝不动。
slice():浅拷贝小能手
const arr = [1, 2, 3, 4, 5];
arr.slice(1, 3); // [2, 3] - 包左不包右
arr.slice(1); // [2, 3, 4, 5] - 从1到结束
arr.slice(-2); // [4, 5] - 倒数两个
join():数组转字符串
['a', 'b', 'c'].join(); // "a,b,c"
['a', 'b', 'c'].join('-'); // "a-b-c"
['a', 'b', 'c'].join(''); // "abc"
concat():数组合并
[1, 2].concat([3, 4], 5); // [1, 2, 3, 4, 5]
indexOf() & lastIndexOf():找位置
const arr = [1, 2, 3, 2, 1];
arr.indexOf(2); // 1 - 第一个2的位置
arr.lastIndexOf(2); // 3 - 最后一个2的位置
arr.indexOf(4); // -1 - 找不到
ES7 includes():是否存在
const arr = [1, 2, 3, NaN];
arr.includes(2); // true
arr.includes(4); // false
arr.includes(NaN); // true - 这个比indexOf强!
2.3 遍历方法:12个方法让你玩转数组
forEach():最常用的遍历
[1, 2, 3].forEach((item, index) => {
console.log(`索引${index}的值是${item}`);
});
every() & some():条件检查
// every:所有元素都要满足条件
[2, 4, 6].every(x => x % 2 === 0); // true
// some:至少一个满足条件
[1, 2, 3].some(x => x % 2 === 0); // true
filter():过滤高手
const numbers = [1, 2, 3, 4, 5, 6];
// 找出所有偶数
const evens = numbers.filter(x => x % 2 === 0); // [2, 4, 6]
map():数据转换
const users = [
{ name: '小明', age: 18 },
{ name: '小红', age: 20 }
];
// 提取姓名数组
const names = users.map(user => user.name); // ['小明', '小红']
reduce():最强大的方法
这个有点难,但学会了是真香!
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((total, num) => total + num, 0); // 15
// 统计字符出现次数
const chars = ['a', 'b', 'a', 'c', 'b', 'a'];
const count = chars.reduce((obj, char) => {
obj[char] = (obj[char] || 0) + 1;
return obj;
}, {}); // {a: 3, b: 2, c: 1}
ES6 find() & findIndex():按条件查找
const users = [
{ id: 1, name: '小明' },
{ id: 2, name: '小红' }
];
users.find(user => user.id === 2); // {id: 2, name: '小红'}
users.findIndex(user => user.id === 2); // 1
ES6 keys() & values() & entries():遍历键值
const arr = ['a', 'b', 'c'];
// 遍历索引
[...arr.keys()]; // [0, 1, 2]
// 遍历值(跟数组本身差不多)
[...arr.values()]; // ['a', 'b', 'c']
// 遍历键值对
[...arr.entries()]; // [[0, 'a'], [1, 'b'], [2, 'c']]
实战技巧:这些组合拳让你代码更优雅
链式调用:函数式编程的魅力
const result = [1, 2, 3, 4, 5, 6]
.filter(x => x % 2 === 0) // [2, 4, 6]
.map(x => x * 2) // [4, 8, 12]
.reduce((sum, x) => sum + x, 0); // 24
数组去重的N种方法
// 方法1:Set(最简洁)
[...new Set([1, 2, 2, 3, 3])]; // [1, 2, 3]
// 方法2:filter + indexOf
[1, 2, 2, 3, 3].filter((item, index, arr) =>
arr.indexOf(item) === index
);
// 方法3:reduce
[1, 2, 2, 3, 3].reduce((unique, item) =>
unique.includes(item) ? unique : [...unique, item], []
);
性能小贴士
- 大数据量:避免在循环中修改数组
- 查找操作:
includes比indexOf更语义化 - 删除元素:知道长度时用
length = n最快 - 清空数组:
arr.length = 0比arr = []更好
总结
数组方法虽多,但记住这个分类就不乱了:
- 改变原数组:splice、sort、pop、shift、push、unshift、reverse、copyWithin、fill
- 返回新数组:slice、concat、filter、map
- 返回其他值:join、indexOf、includes、reduce、find
记住:知道用什么方法比记住所有方法更重要!