JavaScript算术运算符
章节目录上一篇:《类型转换》下一篇:《比较运算符》
运算符是数学计算中的概念,我们从小学就在学习的+
、-
、*
、/
就是最基础的运算符。
运算符分类
依据参与运算符的操作数的个数,我们习惯上将运算符分为“单目运算符”、“双目运算符”和“三目运算符”。另外一种说法是:“一元运算符”、“二元运算符”和“三元运算符”。
单目运算符
如果一个运算符只操作一个数据,那么它就是单目运算符
负号'-'
就是一个单目运算符,它的作用就是对数字进行正负转换:
let x = 1;
x = -x; //取负值时只操作一个数
alert(x);
双目运算符
如果一个运算符拥有两个操作数,那么它就是双目运算符,例如减号'-'
就是双目运算符,需要操作减数和被减数:
let x = 1;
let y = 2;
alert(x - y); //减号操作两个操作数
这里,同样一个减号
'-'
拥有两种含义,即负号、减号
负号是单目运算符,用于反转数字的符号
减号是双目运算符,用于操作减数和被减数
三目运算符
三目运算符同时操作三个操作数,在JavaScript
中只有一种三目运算符:? :
。
我们会在if else
章节详细介绍三目运算符的用法。
算术运算符
算术运算符包括以下几种:
- 加法
+
- 减法
-
- 乘法
*
- 除法
/
- 求模
%
(求余) - 求幂
**
(次方)
前面四个基础运算都特别的简单,举例带过:
let x = 1;
let y = 2;
alert(x + y); //3
alert(x - y); //-1
alert(x * y); //2
alert(x / y); //0.5
求模%(取余)
取余数的运算符是%
,虽然符号比较奇怪,但实际上和百分号毫无关联。
x % y
的意思就是用x
整除y
的余数。
举个栗子:
let x = 9;
let y = 2;
alert(x % y); // 1
alert(y % x); // 2
求幂**
(次方、指数)
幂运算符**
用于指数运算,(x**y
)指的是x
的y
次方,也就是x。
举个栗子:
alert(2 ** 3); // 8
alert(2 ** 0.5); // 1.414... √2
alert(0.3 ** 2); // 0.09
字符串拼接运算符 +
加号'+'
运算符,除了简单的算术加和之外,还能以双目运算符的身份参与字符串拼接运算。
举个栗子:
let str1 = "Trump";
let str2 = "Crazy";
let str3 = str1 + str2;//用加号拼接字符串
alert(str3);
加号作为拼接运算符也是上节《类型转换》中字符串不会隐式转换成数字,参与加法运算的原因。
注意
任意一个参与运算的变量是字符串,那么另外一个操作数也会被转换成字符串。
举个栗子:
let num = 996;
let str = "JavaScript";
alert(num + str); // "996JavaScript"
alert(str + num); // "JavaScript996"
这种特性和其他语言稍有区别
例如在C++
、Java
中,数字加字符串会出现类型不兼容错误。
另一种情况:
let num1 = 1;
let num2 = 2;
let str = "JavaScript"
alert(num1 + num2 + str); //(1) "3JavaScript"而不是"12JavaScript"
alert(str + num1 + num2); //(2) "JavaScript12"而不是"JavaScript3"
请注意这里有一些和直觉不同的结果,造成这样结果的原因是表达式的计算都是从左向右进行的。
(1)
处的计算是先进行1 + 2 = 3
,然后再执行3 + "JavaScript" = "3JavaScript"
。
(2)
处的计算是先进行"JavaScript" + 1 = "JavaScript1"
,然后再执行"JavaScript1" + 2 = "JavaScript12"
。
数字转运算符 +
加号同样可以作为单目运算符使用,如果作为正号用在数学运算中,是没有任何作用的:
let num1 = 1;
alert(+num1);
let num2 = -1;
alert(+num2);
但是作为数字转运算符用在非数字类型的变量上,可以起到类型转换的目的:
alert(typeof +"123"); //Number
alert(typeof +true); //Number
alert(typeof +undefined) //Number
alert(+"123"); //123
alert(+true); //1
alert(+undefined) //NaN
当'+'
以单目运算符的身份用在非数字类型变量上,就能产生和类型转换函数Number(...)
同样的效果,但是更加简洁。
例如,使用加号实现字符串的数学运算:
let x = "1";
let y = "2";
alert(x + y); //字符串拼接'12'
alert(+x + +y); //数字1+2=3
虽然包含大量加号的表达式看起来非常奇怪,但是,如果熟悉了JavaScript
的语法格式,这么做简洁且高效。
上例中可以看到,单目运算符是在双目运算符之前执行的,这是运算符之间优先级引起的,JavaScript
引擎总是先计算优先级较高的运算符。
运算符优先级
当一个表达式拥有多个运算符时,执行的顺序则由运算符的优先级决定。
小学我们就知道在表达式1 + 2 * 3
中,先做乘除后做加减,这就说明乘除法比加减法优先级更高。
圆括号可以改变运算顺序,在运算符中拥有最高优先级,如果我们对现有的运算顺序不满意,可以用圆括号来修改顺序,例如(1 + 2) * 3
就可以让加法在乘法之前计算。
JavaScript
有许多运算符,每个运算符都有对应的优先级数字。如果优先级相同,则按照由左至右的顺序执行。
优先级 | 名称 | 符号 |
15 | 单目加号 |
|
15 | 单目负号 |
|
14 | 求幂 |
|
13 | 乘号 |
|
13 | 除号 |
|
12 | 加号 |
|
12 | 减号 |
|
2 | 赋值符 |
|
上表可以看出,单目运算符的加减号要高于双目运算符的加减号,这也是为什么在表达式中会先计算单目运算符的原因。
赋值运算符 =
等号=
本身也是一个运算符,它的作用就是将等号右边的表达式结果赋值给左边的变量。
这也是为什么等号的优先级比较低的缘故,这样就可以保证所有右侧的计算全部完成后再向左侧赋值。
let result = 1 + 2 ** 3;
alert(result); // 9
或者,直接将一个变量的值赋给另外一个变量:
let x = 666;
let y = x;
alert(y); // 666
同一个表达式中,赋值运算符可以不止一个:
let a = 1;
let b = 2;
let c = a * (a = a + b);
alert(c); // 3
alert(a); // 3
如果我们以结果逆向推算表达式的计算过程,可以发现,引擎依旧是从左向右运算,首先将第一个a
替换为1
,也就是1 + (a = a + b)
,然后计算括号内的内容,得到1 + (a = 1 + 2)
,最后再赋值给c
。
强调: 绝对不推荐编写代码的时候采用这种方式,不仅容易出错,而且会得罪队友。
链式赋值
赋值运算符也可以在一行中赋值多个变量:
let a, b, c;
a = b = c = 1 + 1;
alert(a); // 2
alert(b); // 2
alert(c); // 2
同样的,为了可读性起见,建议每行只做一次赋值:
a = 1 + 1;
b = a;
c = a;
我们在写代码的时候,炫技虽然是乐趣之一
但是简朴、稳定、可读的代码更是老手的典范
真正的高手往往能够写出小白都能看懂的代码
自增、自减运算符
我们在编程过程中,会经常遇到变量的加一、减一操作,尤其是在循环语句中。
在正常情况下,我们可以通过如下方式实现计算:
let a = 1;
let b = 2;
a = a + 1;
b = b - 1;
alert(a); // 2
alert(b); // 1
JavaScript
提供了一种更为简洁高效的做法:
- 自增运算符
++
let a = 1;
a++; // 作用和 a = a + 1;相同
alert(a);
- 自减运算符
--
let b = 9;
b--; // 作用和 b = b - 1;相同
alert(b);
自增自减运算符还可以放置在变量的前面:
let a = 1;
++a; // 作用和 a = a + 1;相同
alert(a);
let b = 9;
--b; // 作用和 b = b - 1;相同
alert(b);
前置和后置的 ++
/--
都会完成变量的自增、自减操作,但是二者的返回值完全不同。
每个运算符都会有一个返回值,自增自减同样如此,前置的自增/减运算符会先执行自增/减计算,然后返回计算后的值,后置的自增/减运算符会先返回变量值本身,然后在进行自增/减运算。
let a = 1;
let b = ++a;
alert(b); // b = 2
前置的++
运算符会先执行自增运算,然后返回自增后的结果,所以b = 2
。
let a = 1;
let b = a++;
alert(b); // b = 1;
后置的++
运算符会先返回a
的值,然后再执行自增运算,所以b = 1
。
自增/减运算符也可以放在表达式中使用,由于优先级较高,没有必要使用圆括号:
let a = 1;
let b = 2;
let c = a++ + --b;
alert(c); // 2
虽然,这样写也没有太大问题,但是依旧建议大家将代码写为以下形式:
let a = 1;
let b = 2;
--b;
let c = a + b;
a++;
alert(c); // 2
位运算符
以上运算符都是站在十进制的基础上,而位运算符则是站在32位二进制的基础上进行的计算,大部分的编程语言都支持这些运算符。
位运算符包括:
- 与
&
- 或
|
- 非
~
- 异或
^
- 左移
<<
- 右移
>>
- 无符号右移
>>>
为了计算方便,计算机中所有的数据都是以二进制的方式存储再计算机中的,也就是只包含0
和1
的数字串。
由于位运算符使用的场景很少,且需要大量的理论知识作为铺垫,所以不在本文细讲,后面会专门出一节详细讲解原码、反码和补码,同时介绍它们与位运算的关系。
运算符简写
由于我们经常需要对同一个变量做运算,同时还要将运算的结果存储在变量中,例如:
let a = 1;
a = a + 1;
a = a - 1;
a = a * 2;
a = a / 2;
可以使用运算符简写 += -= *= /=
,提高编程效率,以上代码完全等价于下面的代码:
let a = 1;
a += 1; // a = a + 1;
a -= 1; // a = a - 1;
a *= 2; // a = a * 2;
a /= 2; // a = a / 2;
此类简写的运算符优先级和=
完全相同,所以会在右侧表达式计算完成之后运行:
let a = 2;
a *= 1 + 1;
alert(a); // 4
逗号运算符
逗号也是一种运算符,只不过大多数人都没有意识到这一点,常用于编写更简短的代码。
逗号运算符可以让我们在同一行执行多个表达式计算,表达式之间用逗号分割,但是只有最后一个表达式会返回结果
举个栗子:
let a = (1 + 2, 3 + 4);
alert( a ); // 7(3 + 4 的结果)
第一个语句1 + 2
虽然计算了,但是并不会返回结果,随后计算3 + 4
,并返回计算结果。
逗号运算符的优先级非常低,比'='
还要低,因此上面你的例子中圆括号非常重要,否则逗号还没有发挥作用a
就会被赋值3
,3 + 4
的结果会被丢弃。
为什么我们需要这样一个运算符,它只返回最后一个值呢?
有时候,人们会使用它把几个行为放在一行上来进行复杂的运算。
举个例子:
// 一行上有三个运算符
for (a = 1, b = 3, c = a * b; a < 10; a++) {
...
}
这样的技巧在许多JavaScript
框架中都有使用,这也是为什么我们提到它。但是通常它并不能提升代码的可读性,使用它之前,我们要想清楚。
课后作业
- 计算程序输出
let a = 1;
let b = 2;
let c = ++a * b--;
alert(c);//?
- 补全程序
let r = 2 ___ 3;
alert(r);//如果此处输出8,下划线上应该是什么?
- 修改程序
let a = prompt("请输入一个数字?", 8);
let b = prompt("请输入另一个数?", 7);
alert(a*b); // 如果我希望输出56,程序应该怎么改?
- 判断输出
let x = '1';
x += +x++ + ++x;
alert(x); //?