1. 起因
上篇文章提到,由于业务上使用频率的原因,导致自己对数组的某些 api 不甚了解,在我搞清楚了 slice 、splice、reduce 后,信心满满,自以为已经数组大成了,当时的聊天记录足以证明我的信心。
但是,最近有几个数组的使用场景,结合看了一些大佬总结数组的文章,让我了解到,对一些方法的理解还有不足,需要继续巩固一下。
2. 场景
2.1. sort 的参数与规则
[1, 3, 2].sort((a, b) => a - b);
[1, 3, 2].sort((a, b) => b - a);
a - b 与 b - a 哪个是从小到大?
从 MDN 上的定义来看
- 如果
a - b < 0则a在b的前面 - 如果
a - b > 0则b在a的前面 - 如果
a - b == 0则a与b可以认为不会交换
在加上 console 看看 a , b 到底是什么?
已 a - b 为例
可以看到 a , b 在第一次比较时分别是 3, 1 (b 是第一个元素,a 是第二个元素)
3 - 1 > 0 及 a - b > 0 , 所以 b 应该在 a 的前面
所以 1 应该在 3 的前面, 可以看出是从小到大
之前我 想当然 的以为, a 应当是第一个元素 1, b 是第二个元素 3 , a - b < 0 , 也能推出是从小到大,但是这样一来,对参数的理解就与 chrome 浏览器实现不一致了。
PS: 感谢 @shyg 的指正,以上仅代表了 chrome 浏览器,部分浏览器参数的逻辑为下表,有兴趣的小伙伴可以自己去试试相应的浏览器~
| 浏览器 | 参数顺序 |
|---|---|
| chrome | b 是前面的元素,a 是后面的元素 |
| firefox | a 是前面的元素,b 是后面的元素 |
| safari | 同 chrome |
| Microsoft Edge | 同 chrome |
sort 方法还有一个注意点是虽然函数的返回值是一个排列好的数据。但是原数组已经发生了改变,在实际使用中一定要注意,避免出现以下情况!
var arr = [1, 2, 3];
var arr1 = arr.sort((a, b) => b - a);
// 然后以为 arr 还是原数组的情况 ~
2.2. 在 for 循环或 forEach 中 return
for 循环中 return 确实可以跳出循环并返回结果
forEach 却并没有这个功效
var array = [1, 2, 3, 4, 5];
// 假设需要实现一个 get 方法,获取数组中第一个大于 3 的数字
function get(arr) {
// 可以这么写 ✅
for (let i of arr) {
if (i >= 3) {
return i;
}
}
return null;
// 错误 ❌
arr.forEach((item) => {
if (item >= 3) {
return item;
}
});
return null;
// 错误 ❌
const result = arr.forEach((item) => {
if (item >= 3) {
return item;
}
return null;
});
return result;
}
2.3. 更准确的 includes
这个是看一篇高赞数组总结文章学习到的
includes 可以判断数组中是否包含 NAN,但是 indexOf 不行
2.4. 容易遗忘的 unshift
业务中从头部插入一个数组有挺多的场景,昨天在业务项目中 review 了一下
发现写法挺多的
[1].concat([2, 3]);
[1, ...[2,3]]
如果可以直接改变原数组,直接用 [2, 3].unshift(1) 也是个不错的选择
2.5. reduce 的孪生兄弟
看 api 时发现有一个 reduceRight 是从后往前执行 reduce 的可以当成 [].reverse().reduce 使用。
在观察 reduceRight 方法时,突然对 reduce/reduceRight 第三个参数有些好奇。想知道到底代表的是哪一位的下标。
经过实践发现,第三个参数代表的是第二个参数在数组中的下标,且与从前到后、或从后到前的遍历顺序无关
比如
- 上面的
reduceRight首次遍历,当前项是5,所以输出的下标是4 - 下面没有加默认值的
reduceRight首次遍历,当前项是4, 所以输出的下标是3
2.6. Mock数据高手 fill
Array(5).fill(1); // [1, 1, 1, 1, 1]
如果我们需要制造一个比较长的对象数组,用于页面滚动相关测试时,使用 fill 会让我们不用频繁的复制粘贴
这个收录进来的原因:是之前没认真想过这个方法的执行原理,有时候着急会写出 [].fill(5) 这样的 错误 写法,认为是在数组里填充 5 个元素~
2.7. for of 的使用场景
如果我们不需要使用到循环的下标,使用 for of 可以让代码看上去更简洁,有以下场景可以使用
// 第一种情况,找到数组中符合要求的一项
let arr = [];
let findItem = defaultValue;
// 正确 ✅
for (let item of arr) {
if (item === xxx) {
findItem = item;
break;
}
}
// 使用find也是可以的 ✅
const findItem = arr.find((item) => item === xxx) || defaultValue;
// 第二种情况,遍历数组,用里面的数据配合原生api组合调用
let arr = [];
// 例如使用数组元素,绘制canvas的场景
for (let item of arr) {
document.getElementById('canvas').drawImage(item.url, item.x, item.y, item.width, item.height)
}
2.8. every 与 some
every 需要数组中所有的项满足条件,才可返回 true
some 需要数组中只有一项满足条件,即可返回 true
// 他们均可以让以下场景的语义化更简洁
const arr = [100, 100, 100];
const arr1 = [100, 0, 0];
// for 循环写法
let isAllRight = true;
for (let item of arr) {
if (item !== 100) {
isAllRight = false;
break;
}
}
let hasAllRight = false;
for (let item of arr) {
if (item === 100) {
hasAllRight = true;
break;
}
}
// ✅ every 写法
const isAllRight = arr.every((item) => item === 100);
// ✅ some 写法
const hasAllRight = arr1.some((item) => item === 100);
// 如果数组是如上的简单结构,其实 some 可以用 includes/indexOf 代替
const hasAllRight = arr1.includes(100);
const hasAllRight = arr1.indexOf(100) > -1;
// 但是如果数组是 [{score: 100}, {score: 100}, {score: 0}] 对象格式,就需要使用 some