数学是生活的基本组成部分,我们离不了它。尤其如此,当我们学习编写 JavaScript 程序(或任何其他语言),我们所做的很多事情都依赖于处理数值数据,计算新值等。你将不会惊讶地认识到 JavaScript 有一套可用的全功能的数学功能。
本文仅讨论你现在需要了解的基本部分。
术语:“一元”(unary)、“二元”(binary)、“操作数”(operand)
在继续之前,我们先了解一些常见术语。
同样的减号 -
也存在二元形式:
let x = 1, y = 3; alert( y - x ); // 2, binary minus subtracts values
正式来说,在上述示例中,我们有两个不同的运算符共享相同的符号:负号运算符(一元运算符,用于取反)和 减法运算符(二元运算符,用于两个数相减)。
数字(Number)类型
在编程中,即使是人人熟知的最普遍的十进制数,也比你想象的要复杂的多。我们使用不同的术语来描述不同类型的十进制数,例如:
- 整数 就是整数,例如 10, 400, 或者 -5.
- 浮点数 (浮点) 有小数点或小数位,例如 12.5,和 56.7786543。
- 双精度双精度是一种特定类型的浮点数,它们具有比标准浮点数更高的精度(这意味着它们精确到更大的小数位数)。
我们甚至有不同类型的数字系统!十进制是基数 10(意味着它在每列使用 0-9),但是我们也有这样的东西:
- 二进制——计算机的最基础语言——0 和 1
- 八进制——基数 8,每列使用 0-7
- 十六进制——基数 16,每列使用 0-9,然后使用 a-f。在 CSS 中设置颜色时,可能会遇到这些数字。
在你开始担心你的大脑混乱之前,先停下来吧!一开始,我们将在本课程中坚持使用十进制数; 你很少会遇到需要开始考虑其他类型的情况,如果有的话。
第二个好消息是,与其他一些编程语言不同,JavaScript 只有一个数据类型用来表示数字(包括 integers 和 decimals),你猜对了,Number
。这意味着,你在 JavaScript 中处理的任何类型的数字,都以完全相同的方式处理它们。
我们已经在javascript数据类型中解释了 Number(数字)数据类型
算术运算符
运算符 | 名称 | 作用 | 示例 |
---|---|---|---|
+ |
加法 | 两个数相加。 | 6 + 9 |
- |
减法 | 从左边减去右边的数。 | 20 - 15 |
* |
乘法 | 两个数相乘。 | 3 * 7 |
/ |
除法 | 用右边的数除左边的数 | 10 / 5 |
% |
求余 (有时候也叫取模) | 在你将左边的数分成同右边数字相同的若干整数部分后,返回剩下的余数 | 8 % 3 (返回 2,8 除以 3 的倍数,余下 2。) |
** |
幂 | 取底数的指数次方,即指数所指定的底数相乘。它在 EcmaScript 2016 中首次引入。 | 5 ** 5 (返回 3125,相当于 5 * 5 * 5 * 5 * 5 。) |
备注: 你以后有时候会看到参与算术计算的数字被称为 操作数 (operands)。
备注: 有时你可能会看到使用较旧的 Math.pow()
方法表达的指数,该方法的工作方式非常相似。例如,在 Math.pow(7, 3)
中,7
是基数,3
是指数,因此表达式的结果是 343
。 Math.pow(7, 3)
相当于 7 ** 3
。
我们可能不需要教你如何做基础数学,但我们想测试你对所涉及的语法的理解。尝试将下面的示例输入到开发者工具 JavaScript 控制台中。
取余运算符 %
取余运算符 %
,尽管看起来像百分比,但与百分比无关。
表达式 a % b
的结果是 a
被 b
整除后的余数。
例如:
alert( 5 % 2 ); // 1, the remainder of 5 divided by 2 alert( 8 % 3 ); // 2, the remainder of 8 divided by 3 alert( 8 % 4 ); // 0, the remainder of 8 divided by 4
幂运算 **
幂运算符 a ** b
表示 a
的 b
次方。
在数学中,我们通常写作 aᵇ
。
例如:
alert( 2 ** 2 ); // 2² = 4 alert( 2 ** 3 ); // 2³ = 8 alert( 2 ** 4 ); // 2⁴ = 16
就像在数学中一样,幂运算符也适用于非整数数值。
例如,平方根 可以表示为 1/2 次幂:
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root) alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
使用二元运算符 +
进行字符串连接
让我们看看 JavaScript 运算符的一些超出学校算术范围的特性。
通常,加号 +
用于数值相加。
但如果二元 +
作用于字符串,它会合并(连接)它们:
let s = "my" + "string"; alert(s); // mystring
请注意,如果任一操作数是字符串,则另一个操作数也会被转换为字符串。
例如:
alert( '1' + 2 ); // "12" alert( 2 + '1' ); // "21"
可以看到,无论第一个操作数还是第二个操作数是字符串,都会触发字符串连接。
这里有一个更复杂的例子:
alert(2 + 2 + '1' ); // "41" and not "221"
在这里,运算符是依次执行的。第一个 +
先对两个数字求和,结果是 4
,然后下一个 +
将字符串 '1'
添加进去,所以相当于:
4 + '1' = '41'
。
alert('1' + 2 + 2); // "122" and not "14"
在这里,第一个操作数是一个字符串,编译器会将另外两个操作数也视为字符串处理。数字 2
会与字符串 '1'
连接,因此变成了 '1' + 2 = "12"
,然后 "12" + 2 = "122"
。
二元 +
是唯一一个以这种方式支持字符串的运算符。其他算术运算符仅适用于数字,并且始终将它们的操作数转换为数字。
下面是减法和除法的示例:
alert( 6 - '2' ); // 4, converts '2' to a number alert( '6' / '2' ); // 3, converts both operands to numbers
数字转换,一元加号运算符(Unary +)
加号 +
有两种形式:我们上面使用的二元形式和一元形式。
一元加号,换句话说,+
运算符作用于单一值时,对数字没有任何影响。但如果操作数不是数字,一元加号会将其转换为数字。
例如:
// No effect on numbers let x = 1; alert( +x ); // 1 let y = -2; alert( +y ); // -2 // Converts non-numbers alert( +true ); // 1 alert( +"" ); // 0
它实际上和 Number(...)
做的事情相同,只是更简短。
将字符串转换为数字的需求非常常见。例如,如果我们从 HTML 表单字段获取值,它们通常是字符串。那么,如果我们想将它们相加呢?
二元加号会将它们作为字符串相加:
let apples = "2"; let oranges = "3"; alert( apples + oranges ); // "23", the binary plus concatenates strings
如果我们想将它们当作数字处理,就需要先转换它们,再进行相加:
let apples = "2"; let oranges = "3"; // both values converted to numbers before the binary plus alert( +apples + +oranges ); // 5 // the longer variant // alert( Number(apples) + Number(oranges) ); // 5
从数学家的角度来看,多个加号可能显得有些奇怪。但从程序员的角度来看,这没什么特别的:一元加号优先应用,它们将字符串转换为数字,然后二元加号再将它们相加。
为什么一元加号在二元加号之前应用?正如我们将看到的那样,这是因为一元加号的优先级更高。
运算符优先级
运算符优先级
如果一个表达式中包含多个运算符,运算顺序由它们的**优先级(precedence)**决定,换句话说,也就是运算符的默认优先顺序。
我们在学校里都学过,表达式 1 + 2 * 2
中,乘法应在加法之前计算。这就是优先级的体现。我们说乘法的优先级高于加法。
括号会覆盖任何优先级,所以如果我们不满意默认的运算顺序,可以使用括号进行调整。例如:(1 + 2) * 2
。
JavaScript 中有很多运算符,每个运算符都有对应的优先级数字。数字越大,优先级越高,执行越早。如果优先级相同,则按从左到右的顺序执行。
下面是优先级表的一个节选(你不需要记住它,但请注意:一元运算符的优先级高于对应的二元运算符):

如我们所见,“一元加号”的优先级是 14,高于“加法”(二元加号)的 11。
这就是为什么在表达式 +apples + +oranges
中,一元加号会先执行,然后才进行加法运算。
赋值(Assignment)运算符
需要注意,赋值符号 =
也是一个运算符。在优先级表中,它的优先级非常低,为 2。
这就是为什么在我们进行赋值时,比如 x = 2 * 2 + 1
,表达式中的计算会先执行,然后才执行赋值 =
,将结果存储到变量 x
中。
let x = 2 * 2 + 1; alert( x ); // 5
赋值 =
会返回一个值
赋值符号 =
是一个运算符。在 JavaScript 中,所有运算符都会返回一个值。这对 +
和 -
来说显而易见,但对 =
也是如此。
表达式 x = value
会将值写入 x
,然后返回该值。
下面是一个将赋值作为复杂表达式一部分的示例:
let a = 1; let b = 2; let c = 3 - (a = b + 1); alert( a ); // 3 alert( c ); // 0
在上面的示例中,表达式 (a = b + 1)
的结果是赋给 a
的值(也就是 3
)。这个值随后被用于后续的计算。
挺有趣的代码,不是吗?我们应该理解它是如何工作的,因为在一些 JavaScript 库中确实会看到这种写法。
不过,请不要自己写这种代码。这种“技巧”并不会让代码更清晰或者更易读。
链式赋值(Chaining assignments)
另一个有趣的特性是可以进行链式赋值:
let a, b, c; a = b = c = 2 + 2; alert( a ); // 4 alert( b ); // 4 alert( c ); // 4
链式赋值是从右向左进行求值的。首先,最右侧的表达式 2 + 2
被计算出来,然后这个值依次赋给左边的变量:c
、b
和 a
。最终,所有变量拥有相同的值。
不过,为了代码的可读性,最好将这样的代码拆分成几行:
c = 2 + 2; b = c; a = c;
这样更易于阅读,尤其是在快速浏览代码时。
就地修改(Modify-in-place)
我们经常需要对一个变量应用某个运算符,并将新的结果存回这个变量本身。
let n = 2; n = n + 5; n = n * 2;
这种写法可以通过运算符 +=
和 *=
等进行简化:
let n = 2; n += 5; // now n = 7 (same as n = n + 5) n *= 2; // now n = 14 (same as n = n * 2) alert( n ); // 14
几乎所有算术和位运算符都有对应的简写“修改并赋值”运算符,如:/=
、-=
等。
这类运算符的优先级与普通赋值运算符相同,因此它们在大多数其他运算之后执行:
let n = 2; n *= 3 + 5; // right part evaluated first, same as n *= 8 alert( n ); // 16
但是还有一些更复杂的类型,它们提供了有用的快捷方式,可以使你的代码更加清洁和高效。最常见的如下:
运算符 | 名称 | 作用 | 示例 | 等价于 |
---|---|---|---|---|
+= |
加法赋值 | 右边的数值加上左边的变量,然后再返回新的变量。 | x = 3; x += 4; |
x = 3; x = x + 4; |
-= |
减法赋值 | 左边的变量减去右边的数值,然后再返回新的变量。 | x = 6; x -= 3; |
x = 6; x = x - 3; |
*= |
乘法赋值 | 左边的变量乘以右边的数值,然后再返回新的变量。 | x = 2; x *= 3; |
x = 2; x = x * 3; |
/= |
除法赋值 | 左边的变量除以右边的数值,然后再返回新的变量。 | x = 10; x /= 5; |
x = 10; x = x / 5; |
自增和自减运算符
有时候,你需要反复把一个变量加 1 或减 1。这可以方便地使用增量(++
)和递减( --
)运算符来完成。我们在JavaScript 初体验文章的“猜数字”游戏中使用了++,当我们添加 1 到我们的 guessCount 变量来跟踪用户在每次转动之后剩下的猜测时。
语法:
guessCount++;
JavaScript按位运算符
按位运算符将操作数当作 32 位整数 来处理,并在它们的二进制表示层面上进行操作。
这些运算符不仅限于 JavaScript,大多数编程语言都支持它们。

Javascript 逗号运算符(Comma)
逗号运算符 ,
是最少见、最不常见的运算符之一。有时候它被用来写更简短的代码,所以我们需要了解它,以便看懂别人的代码。

运算符优先级
括号中的表达式在表达式的其余部分之前计算,函数在结果用于表达式的其余部分之前执行:
优先级 | 运算符 | 描述 | 例子 |
---|---|---|---|
18 | ( ) | 表达式分组 | (100 + 50) * 3 |
17 | . | 属于成员 | person.name |
17 | [] | 属于成员 | person[“name”] |
17 | ?. | 可选链 ES2020 | x ?. y |
17 | () | 函数调用 | myFunction() |
17 | new | 带参的新建 | new Date(“June 5,2022”) |
16 | new | 不带参的新建 | new Date() |
增量运算符后缀递增在前缀递增之前执行。 |
|||
15 | ++ | 后缀递增 | i++ |
15 | — | 后缀递减 | i– |
14 | ++ | 前缀递增 | ++i |
14 | — | 前缀递减 | –i |
NOT 运算符 |
|||
14 | ! | 逻辑非 | !(x==y) |
14 | ~ | 按位非 | ~x |
一元运算符 |
|||
14 | + | 一元加 | +x |
14 | – | 一元减 | -x |
14 | typeof | 数据类型 | typeof x |
14 | void | 评估空 | void(0) |
14 | delete | 属性删除 | delete myCar.color |
算术运算符指数运算在乘法之前执行。 乘法和除法在加法和减法之前执行。 |
|||
13 | ** | 指数运算 ES2016 | 10 ** 2 |
12 | * | 乘 | 10 * 5 |
12 | / | 除 | 10 / 5 |
12 | % | 除法余数 | 10 % 5 |
11 | + | 加 | 10 + 5 |
11 | – | 减 | 10 – 5 |
11 | + | 串联 | “Bill” + “Gates” |
移位运算符 |
|||
10 | << | 左移 | x << 2 |
10 | >> | 右移(带符号) | x >> 2 |
10 | >>> | 右移(无符号) | x >>> 2 |
关系运算符 |
|||
9 | in | 对象中的属性 | “PI” in Math |
9 | instanceof | 对象的实例 | x instanceof Array |
比较运算符 |
|||
9 | < | 小于 | x < y |
9 | <= | 小于或等于 | x <= y |
9 | > | 大于 | x > y |
9 | >= | 大于或等于 | x >= Array |
8 | == | 等于 | x == y |
8 | === | 严格相等 | x === y |
8 | != | 不相等 | x != y |
8 | !== | 严格不相等 | x !== y |
位运算符 |
|||
7 | & | 按位与 | x & y |
6 | ^ | 按位异或 | x ^ y |
5 | | | 按位或 | x | y |
逻辑运算符 |
|||
4 | && | 逻辑与 | x && y |
3 | || | 逻辑或 | x || y |
3 | ?? | 空值合并 ES2020 | x ?? y |
条件(三元)运算符 |
|||
2 | ? : | 条件 | ? “yes” : “no” |
赋值运算符赋值在其他操作之后执行。 |
|||
2 | = | 简单赋值 | x = y |
2 | : | 冒号赋值 | x: 5 |
2 | += | 加法赋值 | x += y |
2 | -= | 减法赋值 | x -= y |
2 | *= | 乘法赋值 | x *= y |
2 | **= | 指数赋值 | x **= y |
2 | /= | 除法赋值 | x /= y |
2 | %= | 取余赋值 | x %= y |
2 | <<= | 左移赋值 | x <<= y |
2 | >>= | 右移赋值 | x >>= y |
2 | >>>= | 无符号右移 | x >>>= y |
2 | &= | 位与赋值 | x &= y |
2 | |= | 位或赋值 | x |= y |
2 | ^= | 位异或赋值 | x ^= y |
2 | &&= | 逻辑与赋值 | x &= y |
2 | ||= | 逻辑或赋值 | x ||= y |
2 | => | 箭头 | x => y |
2 | yield | 暂停 / 恢复 | yield x |
2 | yield* | 委托运算符 | yield* x |
2 | … | 展开运算符 | … x |
1 | , | 逗号 | x , y |