JavaScript 中的基本数学 — 数值和运算符

在本课程的这一部分,我们将讨论 JavaScript 中的数学——如何使用运算符和其他功能来成功地操纵数值以实现我们的目的。

预备知识 了解 HTMLCSS 基础知识
学习成果
  • JavaScript 中的基本数值运算——加、减、乘、除。
  • 如果数值被定义为字符串,它们就不是数值,并可能导致计算出错。
  • 使用 Number() 将字符串转换为数值。
  • 运算符优先级。
  • 递增和递减。
  • 赋值和比较运算符。
  • 基本的 Math 对象方法,例如 Math.random()Math.floor()Math.ceil()

人人都爱数学

好吧,也许不是。我们中的一些人喜欢数学,另一些人自从在学校学习乘法表和长除法后就讨厌数学,还有一些人介于两者之间。但我们谁也不能否认,数学是生活中不可或缺的一部分,没有它我们寸步难行。当我们学习 JavaScript(或任何其他语言)编程时尤其如此——我们所做的很多事情都依赖于处理数值数据、计算新值等等,所以当你了解到 JavaScript 拥有一整套功能齐全的数学函数时,你不会感到惊讶。

本文只讨论你现在需要了解的基础部分。

数值的类型

在编程中,即使是我们都熟知的普通十进制数系统,也比你想象的要复杂。我们用不同的术语来描述不同类型的十进制数,例如:

  • 整数是没有小数部分的数。它们可以是正数或负数,例如 10、400 或 -5。
  • 浮点数(floats)有小数点和小数位,例如 12.5 和 56.7786543。

我们甚至有不同类型的数制!十进制是基数为 10(意味着每位使用 0-9),但我们还有类似以下的数制:

  • 二进制——计算机的最低级语言;0 和 1。
  • 八进制——基数为 8,每位使用 0-7。
  • 十六进制——基数为 16,每位使用 0-9 然后是 a-f。你可能在 CSS 中设置颜色时遇到过这些数。

在你开始担心大脑要融化之前,请就此打住!首先,在本课程中,我们将只使用十进制数;你很少会需要考虑其他类型的数,如果真的有的话。

第二个好消息是,与其他一些编程语言不同,JavaScript 只有一种数值数据类型,用于表示整数和浮点数——你猜对了,就是 Number。这意味着无论你在 JavaScript 中处理何种类型的数值,处理它们的方式都完全相同。

备注:实际上,JavaScript 还有第二种数值类型,BigInt,用于表示非常非常大的整数。但就本课程而言,我们只需关心 Number 类型的值。

对我来说都是数值

让我们快速地玩一下数值,以重新熟悉我们需要的基本语法。在你的浏览器开发者工具的 JavaScript 控制台中输入下面列出的命令。

  1. 首先,让我们声明几个变量,并分别用一个整数和一个浮点数来初始化它们,然后重新输入变量名以检查一切是否正常:

    js
    const myInt = 5;
    const myFloat = 6.667;
    myInt;
    myFloat;
    
  2. 数值在输入时不需要引号——在继续之前,尝试声明并初始化另外几个包含数值的变量。

  3. 现在让我们检查一下我们最初的两个变量是否是相同的数据类型。JavaScript 中有一个名为 typeof 的运算符可以做到这一点。如下所示输入以下两行:

    js
    typeof myInt;
    typeof myFloat;
    

    两种情况下都应该返回 "number"——这比不同类型的数值有不同的数据类型,需要用不同方式处理要简单得多。太好了!

有用的 Number 方法

Number 对象的一个实例代表了你在 JavaScript 中会用到的所有标准数值,它上面有许多有用的方法可以用来操作数值。我们在本文中不会详细介绍这些方法,因为我们希望将其作为入门介绍,目前只涵盖真正的基础知识;但是,在你读完这个模块几次后,值得去查看对象参考页面,了解更多可用的方法。

例如,要将你的数值四舍五入到固定的小数位数,请使用 toFixed() 方法。在你的浏览器的控制台中输入以下几行:

js
const lotsOfDecimal = 1.7665849587;
lotsOfDecimal;
const twoDecimalPlaces = lotsOfDecimal.toFixed(2);
twoDecimalPlaces;

转换为数值数据类型

有时你可能会遇到以字符串类型存储的数值,这使得对其进行计算变得困难。这最常发生在数据被输入到表单输入框中,且输入框类型为文本时。有一个解决方法——将字符串值传入 Number() 构造函数,以返回相同值的数值版本。

例如,尝试在你的控制台中输入这些行:

js
let myNumber = "74";
myNumber += 3;

你得到的结果是 743,而不是 77,因为 myNumber 实际上被定义为一个字符串。你可以通过输入以下内容来测试:

js
typeof myNumber;

要修复这个计算,你可以这样做:

js
let myNumber = "74";
myNumber = Number(myNumber) + 3;

结果就是 77,如最初预期的那样。

算术运算符

算术运算符用于在 JavaScript 中执行数学计算。

运算符 名称 用途 示例
+ 加法 将两个数相加。 6 + 9
- 减法 左边的数减去右边的数。 20 - 15
* 乘法 将两个数相乘。 3 * 7
/ 除法 左边的数除以右边的数。 10 / 5
% 余数(有时称为模)

返回左边的数除以右边的数后,剩余的余数。

8 % 3(返回 2,因为 3 除 8 得 2,余 2)。

** 将一个底数提升到指数次幂,即底数自乘指数次。 5 ** 2(返回 25,与 5 * 5 相同)。

备注:你有时会看到参与算术运算的数被称为操作数

备注:你有时可能会看到使用旧的 Math.pow() 方法来表示幂运算,它的工作方式非常相似。例如,在 Math.pow(7, 3) 中,7 是底数,3 是指数,所以表达式的结果是 343Math.pow(7, 3) 等同于 7**3

我们可能不需要教你如何做基础数学,但我们想测试你对所涉及语法的理解。尝试在你的开发者工具的 JavaScript 控制台中输入以下示例,以熟悉语法。

  1. 首先尝试输入一些你自己的简单示例,例如:

    js
    10 + 7;
    9 * 8;
    60 % 3;
    
  2. 你也可以尝试在变量中声明和初始化一些数值,并尝试在求和中使用它们——在求和中,这些变量的行为将完全等同于它们所持有的值。例如:

    js
    const num1 = 10;
    const num2 = 50;
    9 * num1;
    num1 ** 3;
    num2 / num1;
    
  3. 本节最后,尝试输入一些更复杂的表达式,例如:

    js
    5 + 10 * 3;
    (num2 % 9) * num1;
    num2 + num1 / 8 + 2;
    

最后一组计算的某些部分可能没有给你预期的结果;下面的部分很可能会解释为什么。

运算符优先级

让我们看一下上面最后一个例子,假设 num2 的值是 50,num1 的值是 10(如上文最初所述):

js
num2 + num1 / 8 + 2;

作为人类,你可能会将其读作“50 加 10 等于 60”,然后“8 加 2 等于 10”,最后“60 除以 10 等于 6”

但浏览器会先算“10 除以 8 等于 1.25”,然后“50 加 1.25 加 2 等于 53.25”

这是因为运算符优先级——在计算一个计算结果(在编程中称为表达式)时,某些运算符会比其他运算符先被应用。JavaScript 中的运算符优先级与学校数学课上教的一样——乘法和除法总是先做,然后是加法和减法(计算总是从左到右进行)。

如果你想覆盖运算符优先级,你可以用括号括起你希望优先处理的部分。所以要得到结果 6,我们可以这样做:

js
(num2 + num1) / (8 + 2);

尝试在控制台中输入上一行来测试一下。

备注:所有 JavaScript 运算符及其优先级的完整列表可以在运算符优先级中找到。

递增和递减运算符

有时你会想对一个数值变量重复地加一或减一。这可以方便地使用递增 (++) 和递减 (--) 运算符来完成。我们在初探 JavaScript 文章中的“猜数字”游戏中使用了 ++,当时我们给 guessCount 变量加 1,以跟踪用户每轮后还剩下多少次猜测机会。

js
guessCount++;

让我们在你的控制台中试一下这些。首先,请注意你不能将它们直接应用于一个数,这可能看起来很奇怪,但我们是在给一个变量赋一个新的更新值,而不是对值本身进行操作。以下代码会返回一个错误:

js
3++;

所以,你只能递增一个已存在的变量。试试这个:

js
let num1 = 4;
num1++;

好了,第二个奇怪之处!当你这样做时,你会看到返回值为 4——这是因为浏览器会返回当前值,然后再递增变量。如果你再次返回变量的值,你就会看到它已经被递增了:

js
num1;

-- 也是如此:尝试以下代码:

js
let num2 = 6;
num2--;
num2;

备注:你可以让浏览器反过来做——先递增/递减变量,然后再返回值——方法是将运算符放在变量的开头而不是末尾。再次尝试上面的例子,但这次使用 ++num1--num2

赋值运算符

赋值运算符是给变量赋值的运算符。我们已经多次使用了最基本的一个,=——它将右边的值赋给左边的变量:

js
let x = 3; // x contains the value 3
let y = 4; // y contains the value 4
x = y; // x now contains the same value y contains, 4

但还有一些更复杂的类型,它们提供了有用的快捷方式,使你的代码更整洁、更高效。最常见的如下所示:

运算符 名称 用途 示例 快捷方式
+= 加法赋值 将右边的值加到左边变量的值上,然后返回新的变量值 x += 4; x = x + 4;
-= 减法赋值 从左边变量的值中减去右边的值,并返回新的变量值 x -= 3; x = x - 3;
*= 乘法赋值 将左边变量的值乘以右边的值,并返回新的变量值 x *= 3; x = x * 3;
/= 除法赋值 将左边变量的值除以右边的值,并返回新的变量值 x /= 5; x = x / 5;

尝试在你的控制台中输入一些上面的例子,以了解它们是如何工作的。在每种情况下,看看你是否能在输入第二行之前猜出值是多少。

注意,你完全可以在每个表达式的右侧使用其他变量,例如:

js
let x = 3; // x contains the value 3
let y = 4; // y contains the value 4
x *= y; // x now contains the value 12

备注:还有许多其他可用的赋值运算符,但这些是你现在应该学习的基础。

调整画布盒子的大小

在这个练习中,你将操作一些数值和运算符来改变一个盒子的大小。这个盒子是使用一个名为 Canvas API 的浏览器 API 绘制的。无需担心它是如何工作的——现在只需专注于数学部分。盒子的宽度和高度(以像素为单位)由变量 xy 定义,它们最初都被赋予了 50 的值。

js
const canvas = document.getElementById("canvas");
const para = document.querySelector("p");
const ctx = canvas.getContext("2d");

// Edit the following two lines ONLY
let x = 50;
let y = 50;

ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "green";
ctx.fillRect(10, 10, x, y);
para.textContent = `The rectangle is ${x}px wide and ${y}px high.`;

通过点击“运行”按钮在 MDN Playground 中打开以上示例,然后按照下面的说明列表,在每种情况下使用特定的运算符和/或值,使盒子放大/缩小到特定尺寸:

  • 更改计算 x 的那一行,使盒子的宽度仍然是 50px,但是 50 是使用数字 43 和 7 以及一个算术运算符计算出来的。
  • 更改计算 y 的那一行,使盒子的高度是 75px,但是 75 是使用数字 25 和 3 以及一个算术运算符计算出来的。
  • 更改计算 x 的那一行,使盒子的宽度是 100px,但是 100 是使用三个数字以及减法和除法运算符计算出来的。
  • 更改计算 y 的那一行,使盒子的高度是 200px,但是 200 是使用数字 2 和 x 以及乘法运算符计算出来的。

如果把代码弄乱了也不用担心。你随时可以按下“重置”按钮重新开始。

比较运算符

有时我们会想运行真/假测试,然后根据测试的结果采取相应的行动——要做到这一点,我们使用比较运算符

运算符 名称 用途 示例
=== 严格相等 测试左值和右值是否完全相同 5 === 2 + 4
!== 严格不相等 测试左值和右值是否完全相同 5 !== 2 + 3
< 小于 测试左值是否小于右值。 10 < 6
> 大于 测试左值是否大于右值。 10 > 20
<= 小于或等于 测试左值是否小于或等于右值。 3 <= 2
>= 大于或等于 测试左值是否大于或等于右值。 5 >= 4

备注:你可能会看到一些人在他们的相等和不相等测试中使用 ==!=。这些在 JavaScript 中是有效的运算符,但它们与 ===/!== 不同。前一种版本测试值是否相同,但不测试值的数据类型是否相同。后一种严格版本则同时测试值和数据类型的相等性。严格版本往往能减少错误,所以我们推荐你使用它们。

如果你尝试在控制台中输入其中一些值,你会看到它们都返回 true/false 值——就是我们在上一篇文章中提到的布尔值。这些非常有用,因为它们允许我们在代码中做决策,并且每当我们想做某种选择时都会用到它们。例如,布尔值可以用来:

  • 根据某个功能是开启还是关闭,在按钮上显示正确的文本标签
  • 如果游戏结束则显示游戏结束消息,如果游戏获胜则显示胜利消息
  • 根据是什么节日季节显示正确的季节性问候
  • 根据选择的缩放级别放大或缩小地图

我们将在未来的文章中学习条件语句时,看看如何编写这样的逻辑。现在,让我们看一个快速的例子:

html
<button>Start machine</button>
<p>The machine is stopped.</p>
js
const btn = document.querySelector("button");
const txt = document.querySelector("p");

btn.addEventListener("click", updateBtn);

function updateBtn() {
  if (btn.textContent === "Start machine") {
    btn.textContent = "Stop machine";
    txt.textContent = "The machine has started!";
  } else {
    btn.textContent = "Start machine";
    txt.textContent = "The machine is stopped.";
  }
}

你可以在 updateBtn() 函数内部看到相等运算符的使用。在这种情况下,我们不是在测试两个数学表达式的值是否相同——我们是在测试按钮的文本内容是否包含某个特定的字符串——但其工作原理是相同的。如果按钮在被按下时当前显示“启动机器”,我们会将其标签更改为“停止机器”,并相应地更新标签。如果按钮在被按下时当前显示“停止机器”,我们会将显示切换回来。

备注:这种在两种状态之间切换的控件通常被称为开关(toggle)。它在一种状态和另一种状态之间切换——灯开,灯关,等等。

总结

在本文中,我们已经涵盖了你目前需要了解的关于 JavaScript 中数值的基础信息。在你的 JavaScript 学习过程中,你会一次又一次地看到数值的使用,所以现在把这部分搞清楚是个好主意。如果你是那些不喜欢数学的人之一,你可以感到欣慰的是,这一章相当简短。

在下一篇文章中,我们将提供一些测试,你可以用它们来检查你对这些信息的理解和掌握程度。

另见