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