咱们从学校里了解到过不少运算符,好比说加号 +
、乘号 *
、减号 -
等。javascript
在这个章节,咱们将关注一些在学校数学课程中没有涵盖的运算符。html
在正式开始前,咱们先简单浏览一下经常使用术语。java
运算元 —— 运算符应用的对象。好比说乘法运算 5 * 2
,有两个运算元:左运算元 5
和右运算元 2
。有时候人们也称其为「参数」。react
若是一个运算符对应的只有一个运算元,那么它是 一元运算符。好比说一元负号运算符(unary negation)-
,它的做用是对数字进行正负转换:程序员
let x = 1;
x = -x;
alert( x ); // -1,一元负号运算符生效
复制代码
若是一个运算符拥有两个运算元,那么它是 二元运算符。减号还存在二元运算符形式:编程
let x = 1, y = 3;
alert( y - x ); // 2,二元运算符减号作减运算
复制代码
严格地说,在上面的示例中,咱们使用一个相同的符号表征了两个不一样的运算符:负号运算符,即反转符号的一元运算符,减法运算符,是从另外一个数减去一个数的二进制运算符。微信
下面,让咱们看一下在学校数学课程范围外的 JavaScript 运算符特性。app
一般,加号 +
用于求和。框架
可是若是加号 +
被应用于字符串,它将合并(链接)各个字符串:编程语言
let s = "my" + "string";
alert(s); // mystring
复制代码
注意:只要其中一个运算元是字符串,那么另外一个运算元也将被转化为字符串。
举个例子:
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"
复制代码
能够看出,字符串在前和在后并不影响这个规则。简单来讲:若是任一运算元是字符串,那么其它运算元也将被转化为字符串。
可是,请注意:运算符的运算方向是由左至右。若是是两个数字,后面再跟一个字符串,那么两个数字会先相加,再转化为字符串:
alert(2 + 2 + '1' ); // "41" 而不是 "221"
复制代码
字符串链接和转化是二元运算符加号 +
的一个特性。其它的数学运算符都只对数字有效。一般,他们会把运算元转化为数字。
举个例子,减法和除法:
alert( 2 - '1' ); // 1
alert( '6' / '2' ); // 3
复制代码
加号 +
有两种形式。一种是上面咱们刚刚讨论的二元运算符,还有一种是一元运算符。
一元运算符加号,或者说,加号 +
应用于单个值,对数字没有任何做用。可是若是运算元不是数字,加号 +
则会将其转化为数字。
例如:
// 对数字无效
let x = 1;
alert( +x ); // 1
let y = -2;
alert( +y ); // -2
// 转化非数字
alert( +true ); // 1
alert( +"" ); // 0
复制代码
它的效果和 Number(...)
相同,可是更加简短。
咱们常常会有将字符串转化为数字的需求。好比,若是咱们正在从 HTML 表单中取值,一般获得的都是字符串。若是咱们想对他们求和,该怎么办?
二元运算符加号会把他们合并成字符串:
let apples = "2";
let oranges = "3";
alert( apples + oranges ); // "23",二元运算符加号合并字符串
复制代码
若是咱们想把它们当作数字对待,咱们须要转化它们,而后再求和:
let apples = "2";
let oranges = "3";
// 在二元运算符加号起做用以前,全部的值都被转化为了数字
alert( +apples + +oranges ); // 5
// 更长的写法
// alert( Number(apples) + Number(oranges) ); // 5
复制代码
从一个数学家的视角来看,大量的加号可能很奇怪。可是从一个程序员的视角,没什么好奇怪的:一元运算符加号首先起做用,他们将字符串转为数字,而后二元运算符加号对它们进行求和。
为何一元运算符先于二元运算符做用于运算元?接下去咱们将讨论到,这是因为它们拥有 更高的优先级。
若是一个表达式拥有超过一个运算符,执行的顺序则由 优先级 决定。换句话说,全部的运算符中都隐含着优先级顺序。
从小学开始,咱们就知道在表达式 1 + 2 * 2
中,乘法先于加法计算。这就是一个优先级问题。乘法比加法拥有 更高的优先级。
圆括号拥有最高优先级,因此若是咱们对现有的运算顺序不满意,咱们可使用圆括号来修改运算顺序,就像这样:(1 + 2) * 2
。
在 JavaScript 中有众多运算符。每一个运算符都有对应的优先级数字。数字越大,越先执行。若是优先级相同,则按照由左至右的顺序执行。
这是一个摘抄自 Mozilla 的 优先级表(你没有必要把这全记住,但要记住一元运算符优先级高于二元运算符):
优先级 | 名称 | 符号 |
---|---|---|
... | ... | ... |
16 | 一元加号 | + |
16 | 一元负号 | - |
14 | 乘号 | * |
14 | 除号 | / |
13 | 加号 | + |
13 | 减号 | - |
... | ... | ... |
3 | 赋值符 | = |
... | ... | ... |
咱们能够看到,「一元运算符加号」的优先级是 16
,高于「二元运算符加号」的优先级 13
。这也是为何表达式 "+apples + +oranges"
中的一元加号先生效,而后才是二元加法。
咱们知道赋值符号 =
也是一个运算符。从优先级表中能够看到它的优先级很是低,只有 3
。
这也是为何,当咱们赋值时,好比 x = 2 * 2 + 1
,全部的计算先执行,而后 =
才执行,将计算结果存储到 x
。
let x = 2 * 2 + 1;
alert( x ); // 5
复制代码
链式赋值也是能够的:
let a, b, c;
a = b = c = 2 + 2;
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4
复制代码
链式赋值由右向左执行。首先执行最右侧表达式 2 + 2
,而后将结果赋值给左侧:c
、b
、a
。最后,全部的变量都共享一个值。
赋值运算符
\"=\"
会返回一个值每一个运算符都有一个返回值。对于以加号
+
或者乘号*
为例的大部分运算符而言,这一点很显然。对于赋值运算符而言,这一点一样适用。语句
x = value
把value
的值写入x
而后返回 x。下面是一个在复杂语句中使用赋值的例子:
let a = 1; let b = 2; let c = 3 - (a = b + 1); alert( a ); // 3 alert( c ); // 0 复制代码
上面这个例子,
(a = b + 1)
的结果是赋给a
的值(也就是3
)。而后该值被用于进一步的运算。这段代码是否是很好玩儿?咱们应该理解它的原理,由于咱们有时会在第三方库中见到这样的写法,但咱们本身不该该这样写。这样的小技巧让代码变得整洁度和可读性都不好。
求余运算符 %
尽管看上去是个百分号,但它和百分数没有什么关系。
a % b
的结果是 a
除以 b
的余数。
举个例子:
alert( 5 % 2 ); // 1 是 5 / 2 的余数
alert( 8 % 3 ); // 2 是 8 / 3 的余数
alert( 6 % 3 ); // 0 是 6 / 3 的余数
复制代码
幂运算符 **
是最近被加入到 JavaScript 中的。
对于天然数 b
,a ** b
的结果是 a
与本身相乘 b
次。
举个例子:
alert( 2 ** 2 ); // 4 (2 * 2)
alert( 2 ** 3 ); // 8 (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
复制代码
这个运算符对于 a
和 b
是非整数的状况依然适用。
例如:
alert( 4 ** (1/2) ); // 2 (1/2 幂至关于开方,这是数学常识)
alert( 8 ** (1/3) ); // 2 (1/3 幂至关于开三次方)
复制代码
对一个数进行加1、减一是最多见的数学运算符之一。
因此,对此有一些专门的运算符:
自加 ++
将变量与 1 相加:
let counter = 2;
counter++; // 和 counter = counter + 1 效果同样,可是更简洁
alert( counter ); // 3
复制代码
自减 --
将变量与 1 相减:
let counter = 2;
counter--; // 和 counter = counter - 1 效果同样,可是更简洁
alert( counter ); // 1
复制代码
注意:
自加/自减只能应用于变量。试一下,将其应用于数值(好比
5++
)则会报错。
运算符 ++
和 --
能够置于变量前,也能够置于变量后。
counter++
。++counter
。二者都作同一件事:将变量 counter
与 1
相加。
那么他们有区别吗?有,但只有当咱们使用 ++/--
的返回值时才能看到区别。
详细点说。咱们知道,全部的运算符都有返回值。自加/自减也不例外。前置形式返回一个新的值,但后置返回原来的值(作加法/减法以前的值)。
为了直观看到区别,看下面的例子:
let counter = 1;
let a = ++counter; // (*)
alert(a); // 2
复制代码
(*)
所在的行是前置形式 ++counter
,对 counter
作自加运算,返回的是新的值 2
。所以 alert
显示的是 2
。
下面让咱们看看后置形式:
let counter = 1;
let a = counter++; // (*) 将 ++counter 改成 counter++
alert(a); // 1
复制代码
(*)
所在的行是后置形式 counter++
,它一样对 counter
作加法,可是返回的是 旧值(作加法以前的值)。所以 alert
显示的是 1
。
总结:
若是自加/自减的值不会被使用,那么二者形式没有区别:
let counter = 0;
counter++;
++counter;
alert( counter ); // 2,以上两行做用相同
复制代码
若是咱们想要对变量进行自加操做,而且 须要马上使用自加后的值,那么咱们须要使用前置形式:
let counter = 0;
alert( ++counter ); // 1
复制代码
若是咱们想要将一个数加一,可是咱们想使用其自加以前的值,那么咱们须要使用后置形式:
let counter = 0;
alert( counter++ ); // 0
复制代码
自加/自减和其它运算符的对比
++/--
运算符一样能够在表达式内部使用。它们的优先级比绝大部分的算数运算符要高。举个例子:
let counter = 1; alert( 2 * ++counter ); // 4 复制代码
与下方例子对比:
let counter = 1; alert( 2 * counter++ ); // 2,由于 counter++ 返回的是「旧值」 复制代码
尽管从技术层面上来讲可行,可是这样的写法会下降代码的可阅读性。在一行上作多个操做 —— 这样并很差。
当阅读代码时,快速的视觉「纵向」扫描会很容易漏掉
counter++
,这样的自增操做并不明显。咱们建议「一行一个操做」模式:
let counter = 1; alert( 2 * counter ); counter++; 复制代码
位运算符把运算元当作 32 位整数,并在它们的二进制表现形式上操做。
这些运算符不是 JavaScript 特有的。大部分的编程语言都支持这些运算符。
下面是位运算符:
&
)|
)^
)~
)<<
)>>
)>>>
)这些操做使用得很是少。为了理解它们,咱们须要探讨底层的数字表达形式,如今不是作这个的最好时机。尤为是咱们如今不会马上使用它。若是你感兴趣,能够阅读 MDN 中的 位运算符 相关文章。当有相关实际需求的时候再去阅读是更明智的选择。
咱们常常须要对一个变量进行操做,并把计算获得的新结果存储在这个变量中。
举个例子:
let n = 2;
n = n + 5;
n = n * 2;
复制代码
这个操做能够经过使用运算符 +=
和 *=
进行简化:
let n = 2;
n += 5; // now n = 7 (同 n = n + 5)
n *= 2; // now n = 14 (同n = n * 2)
alert( n ); // 14
复制代码
简短的「修改并替换」 运算符对全部的运算符包括位运算符都有效:/=
、-=
等等。
这些运算符和正常的赋值运算符拥有相同的优先级,所以它们会在其它大部分运算完成以后运行:
let n = 2;
n *= 3 + 5;
alert( n ); // 16(右侧计算首先进行,和 n *= 8 相同)
复制代码
逗号运算符 ,
是最少见最不常使用的运算符之一。有时候它会被用来写更简短的代码,所以为了可以理解代码,咱们须要了解它。
逗号运算符能让咱们处理多个语句,使用 ,
将它们分开。每一个语句都运行了,可是只有最后的语句的结果会被返回。
举个例子:
let a = (1 + 2, 3 + 4);
alert( a ); // 7(3 + 4 的结果)
复制代码
这里,第一个语句 1 + 2
运行了,可是它的结果被丢弃了。随后计算 3 + 4
,而且该计算结果被返回。
逗号运算符的优先级很是低
请注意逗号运算符的优先级很是低,比
=
还要低,所以上面你的例子中圆括号很是重要。若是没有圆括号:
a = 1 + 2, 3 + 4
会先执行+
,将数值相加获得a = 3, 7
,而后赋值运算符=
执行, 'a = 3',而后逗号以后的数值7
不会再执行,它被忽略掉了。至关于(a = 1 + 2), 3 + 4
。
为何咱们须要这样一个运算符,它只返回最后一个值呢?
有时候,人们会使用它把几个操做放在一行上来进行复杂的运算。
举个例子:
// 一行上有三个运算符
for (a = 1, b = 3, c = a * b; a < 10; a++) {
...
}
复制代码
这样的技巧在许多 JavaScript 框架中都有使用,这也是为何咱们提到它。可是一般它并不能提高代码的可读性,使用它以前,咱们要想清楚。
先本身作题目再看答案。
重要程度:⭐️⭐️⭐️⭐️⭐️
如下代码中变量 a
、b
、c
、d
的最终值分别是多少?
let a = 1, b = 1;
let c = ++a; // ?
let d = b++; // ?
复制代码
重要程度:⭐️⭐️⭐
下面这段代码运行完成后,代码中的 a
和 x
的值是多少?
let a = 2;
let x = 1 + (a *= 2);
复制代码
好好思考一下,把它们写下来而后和答案比较一下。
答案以下:
a = 2
b = 2
c = 2
d = 1
let a = 1, b = 1;
alert( ++a ); // 2,前置操做符返回最新值
alert( b++ ); // 1,后置操做符返回旧值
alert( a ); // 2,自加一次
alert( b ); // 2,自加一次
复制代码
答案以下:
a = 4
(乘以 2)x = 5
(至关于计算 1 + 4)现代 JavaScript 教程:开源的现代 JavaScript 从入门到进阶的优质教程。React 官方文档推荐,与 MDN 并列的 JavaScript 学习教程。
在线免费阅读:zh.javascript.info
扫描下方二维码,关注微信公众号「技术漫谈」,订阅更多精彩内容。