理解JavaScript的模运算符

当我刚开始学习编程的时候,我记得觉得取模运算符(%)非常令人困惑。😬

如果你不理解它的作用,它产生的值似乎完全是随机的:

1
2
3
const what = 10 % 4; // 2
const the = 10 % 10; // 0
const heck = 4 % 10; // 4

在这篇博文中,我们将通过改进我们对除法的心智模型来学习这个运算符的工作原理。我们还将介绍这个奇特运算符的一个实际日常用例。

重新思考除法

假设我们有以下数学运算:

1
12 ÷ 4

除法通常会感觉抽象或理论化,但有一种实际的思考方法:我们想将一个数字分成相等大小的组。

12 ÷ 4的计算结果为3,因为每个组正好包含3个项目。从本质上讲我们正在计算每个组中将包含多少个项目。

在上面的示例中我们的被除数是12。使用除法时,它可以用很多不同的方式来分割。

假设我们有以下等式:

1
11 ÷ 4

这个方程式的值为2.75。每组有2个完整的项目,然后是另一个项目的3/4。

如果我们分披萨或蛋糕,这是可行的。但如果这些物品是不可摧毁的呢?如果我们不能把每一项分解成更小的部分呢?

在这种情况下,我们可以将2个项目放入每个组,然后剩下3个额外的项目放入一个组:

这被称为余数。这是模运算符产生的结果。

在这些数字可以被平均分为几组的情况下(例如,12 ÷ 4),没有余数:

1
12 % 4; // 0

在被除数(要被除的数)不能平均分成几组的情况下,模运算符让我们知道还剩多少:

1
11 % 4; // 3

一个实际用例

所以,我不是数学家,我是一个网页开发者。所有这些数学东西很有趣,但让我们谈谈在网络开发中模运算符如何派上用场。

具体来说,有一种问题似乎我经常遇到,在这个问题中,模运算符提供了完美的解决方案:循环数组。

例如,假设我们有一个包含3种颜色的数组。每秒,我们希望切换到列表中的下一个颜色。当我们达到列表的末尾时,我们希望跳回到第一项:

这是一个令人棘手的问题。假设我们有一个名为timeElapsed的变量,它从0开始,每秒递增1;我们必须以某种方式将这个不断增加的值映射到一个只有3个项的数组上。

基本上,我们需要编写一个函数,它产生以下结果:

1
2
3
4
5
6
7
8
9
10
11
const COLORS = ['red', 'yellow', 'blue'];
getColor({ timeElapsed: 0 }); // 'red'
getColor({ timeElapsed: 1 }); // 'yellow'
getColor({ timeElapsed: 2 }); // 'blue'
getColor({ timeElapsed: 3 }); // 'red'
getColor({ timeElapsed: 4 }); // 'yellow'
getColor({ timeElapsed: 5 }); // 'blue'
getColor({ timeElapsed: 6 }); // 'red'
getColor({ timeElapsed: 7 }); // 'yellow'
getColor({ timeElapsed: 8 }); // 'blue'
// ...And so on, forever

让我们看看取模运算符如何帮助我们解决这个问题:

1
2
3
4
5
const COLORS = ['red', 'yellow', 'blue'];
function getColor({ timeElapsed }) {
const colorIndex = timeElapsed % COLORS.length;
return COLORS[colorIndex];
}

奇迹般地,这正好符合我们的需求!只要timeElapsed是整数,这个方法就会始终返回3种颜色之一。随着timeElapsed的增加,它会循环遍历这3种颜色。

COLORS.length等于3,因为我们的数组中有3种颜色。因此,当timeElapsed从0增加到8时,该函数会执行以下一系列计算:

1
2
3
4
5
6
7
8
9
const colorIndex = 0 % 3; // 0
const colorIndex = 1 % 3; // 1
const colorIndex = 2 % 3; // 2
const colorIndex = 3 % 3; // 0
const colorIndex = 4 % 3; // 1
const colorIndex = 5 % 3; // 2
const colorIndex = 6 % 3; // 0
const colorIndex = 7 % 3; // 1
const colorIndex = 8 % 3; // 2

然后,我们可以使用这个colorIndex从COLORS数组中查找颜色。它保证始终在该数组的可用索引范围内循环。

要理解为什么这样做有效,值得记住我们对除法的新模型:我们试图将timeElapsed分成3个等大的组,不允许任何分数或小数值。余数始终只能是0、1或2。它绝不会是3或更大的值,因为如果余数是3,我们就可以在每组中再放入1个!

本质上,这就好像我们有了创建一个“循环”数组的能力。无论我们底层的timeElapsed值增长到多大,我们都可以让它无限地在COLORS数组中的颜色之间循环。

这只是这个方便运算符的几个实际用例中的一个。