第二期操作符

59 篇文章 1 订阅
40 篇文章 1 订阅

一份汗水,不份收获,一份耕耘,一份成就。——何肖娜
在这里插入图片描述

操作符

ECMA-262 描述了一组可用于操作数据值的操作符,包括数学操作符(如加、减)、位操作符、关
系操作符和相等操作符等。ECMAScript 中的操作符是独特的,因为它们可用于各种值,包括字符串、
数值、布尔值,甚至还有对象。在应用给对象时,操作符通常会调用valueOf()和/或 toString()
法来取得可以计算的值

一元操作符

**只操作一个值的操作符叫一元操作符(**unary operator)。一元操作符是 ECMAScript中最简单的操作符。

一、递增/递减操作符

**递增和递减操作符直接照搬自 C 语言,**但有两个版本:前缀版和后缀版。顾名思义,前缀版就是位
于要操作的变量前头,后缀版就是位于要操作的变量后头。前缀递增操作符会给数值加 1,把两个加号
(++)放到变量前头即可:

let age = 29; 
++age; 

在这里插入图片描述

在这个例子中,前缀递增操作符把 age 的值变成了 30(给之前的值 29 加 1)。因此,它实际上等于
如下表达式:

let age = 29; 
age = age + 1; 

在这里插入图片描述

前缀递减操作符也类似,只不过是从一个数值减 1。使用前缀递减操作符,只要把两个减号(–)
放到变量前头即可:

let age = 29; 
--age; 

在这里插入图片描述

执行操作后,变量 age 的值变成了 28(从 29 减 1)。
无论使用前缀递增还是前缀递减操作符,变量的值都会在语句被求值之前改变。(在计算机科学中,
这通常被称为具有副作用。)请看下面的例子:

let age = 29; 
let anotherAge = --age + 2; 
console.log(age); // 28 
console.log(anotherAge); // 30

在这里插入图片描述

在这个例子中,变量 anotherAge 以 age 减 1 后的值再加 2 进行初始化。因为递减操作先发生,所以 age 的值先变成 28,然后再加 2,结果是 30。

前缀递增和递减在语句中的优先级是相等的,因此会从左到右依次求值。比如:

let num1 = 2; 
let num2 = 20; 
let num3 = --num1 + num2; 
let num4 = num1 + num2; 
console.log(num3); // 21 
console.log(num4); // 21

在这里插入图片描述

这里,num3 等于 21 是因为 num1 先减 1 之后才加 num2。变量 num4 也是 21,那是因为加法使用
的也是递减后的值。

递增和递减的后缀版语法一样(分别是++和–),只不过要放在变量后面。后缀版与前缀版的主要
区别在于,后缀版递增和递减在语句被求值后才发生。在某些情况下,这种差异没什么影响,比如:

let age = 29; 
age++;

在这里插入图片描述

把递增操作符放到变量后面不会改变语句执行的结果,因为递增是唯一的操作。可是,在跟其他操
作混合时,差异就会变明显,比如:

let num1 = 2; 
let num2 = 20; 
let num3 = num1-- + num2; 
let num4 = num1 + num2;
console.log(num3); // 22 
console.log(num4); // 21

在这里插入图片描述

这个例子跟前面的那个一样,只是把前缀递减改成了后缀递减,区别很明显。在使用前缀版的例子
中,num3 和 num4 的值都是 21。而在这个例子中,num3 的值是 22,num4 的值是 21。这里的不同之处
在于,计算 num3 时使用的是 num1 的原始值(2),而计算 num4 时使用的是 num1 递减后的值(1)。
这 4 个操作符可以作用于任何值,意思是不限于整数——字符串、布尔值、浮点值,甚至对象都可
以。递增和递减操作符遵循如下规则。
 对于字符串,如果是有效的数值形式,则转换为数值再应用改变。变量类型从字符串变成数值。
 对于字符串,如果不是有效的数值形式,则将变量的值设置为NaN。变量类型从字符串变成
数值。
 对于布尔值,如果是false,则转换为 0再应用改变。变量类型从布尔值变成数值。
 对于布尔值,如果是 true,则转换为 1 再应用改变。变量类型从布尔值变成数值。
 对于浮点值,加 1 或减 1。
 如果是对象,则调用其valueOf()方法取得可以操作的值。对得到的值应用上述规则。如果是 NaN,则调用toString()并再次应用其他规则。变量类型从对象变成数值。推荐内容柯里化

let s1 = "2"; 
let s2 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 
 s1++; // 值变成数值 3 
s2++; // 值变成 NaN 
b++; // 值变成数值 1 
f--; // 值变成 0.10000000000000009(因为浮点数不精确)
o--; // 值变成-2
console.log(s1)
console.log(s2)
console.log(b)
console.log(f)
console.log(o)

在这里插入图片描述

二、 一元加和减

一元加和减操作符对大多数开发者来说并不陌生,它们在 ECMAScript 中跟在高中数学中的用途一
样。一元加由一个加号(+)表示,放在变量前头,对数值没有任何影响:

let num = 25; 
num = +num; 
console.log(num); // 25 

如果将一元加应用到非数值,则会执行与使用Number()转型函数一样的类型转换:布尔值false
true转换为0 和 1,字符串根据特殊规则进行解析,对象会调用它们的 valueOf()和/或toString()
方法以得到可以转换的值。
下面的例子演示了一元加在应用到不同数据类型时的行为:

let s1 = "01"; 
let s2 = "1.1";
let s3 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 
s1 = +s1; // 值变成数值 1 
s2 = +s2; // 值变成数值 1.1 
s3 = +s3; // 值变成 NaN 
b = +b; // 值变成数值 0 
f = +f; // 不变,还是 1.1 
o = +o; // 值变成数值-1
console.log(s1)
console.log(s2)
console.log(s3)
console.log(b)
console.log(f)
console.log(o)

在这里插入图片描述

一元减由一个减号(-)表示,放在变量前头,主要用于把数值变成负值,如把 1 转换为-1。示例
如下:

let num = 25; 
num = -num; 
console.log(num); // -25

在这里插入图片描述

对数值使用一元减会将其变成相应的负值(如上面的例子所示)。在应用到非数值时,一元减会遵
循与一元加同样的规则,先对它们进行转换,然后再取负值:

let s1 = "01"; 
let s2 = "1.1"; 
let s3 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 
s1 = -s1; // 值变成数值-1 
s2 = -s2; // 值变成数值-1.1 
s3 = -s3; // 值变成 NaN 
b = -b; // 值变成数值 0 
f = -f; // 变成-1.1 
o = -o; // 值变成数值 1
console.log(s1)
console.log(s2)
console.log(s3)
console.log(b)
console.log(f)
console.log(o)

在这里插入图片描述

一元加和减操作符主要用于基本的算术,但也可以像上面的例子那样,用于数据类型转换。

三、 位操作符

接下来要介绍的操作符用于数值的底层操作,也就是操作内存中表示数据的比特(位)。ECMAScript
中的所有数值都以 IEEE 754 64 位格式存储,但位操作并不直接应用到 64 位表示,而是先把值转换为
32 位整数,再进行位操作,之后再把结果转换为 64 位。对开发者而言,就好像只有 32 位整数一样,因为 64 位整数存储格式是不可见的。既然知道了这些,就只需要考虑 32 位整数即可。

64 位整数存储格式是不可见的。既然知道了这些,就只需要考虑 32 位整数即可。
有符号整数使用 32 位的前 31 位表示整数值。第 32 位表示数值的符号,如 0 表示正,1 表示负。这
一位称为符号位(sign bit),它的值决定了数值其余部分的格式。正值以真正的二进制格式存储,即 31
位中的每一位都代表 2 的幂。第一位(称为第 0 位)表示 2^0,第二位表示 2^1
,依此类推。如果一个位是空的,则以0填充,相当于忽略不计。比如,数值18的二进制格式为00000000000000000000000000010010,
或更精简的 10010。后者是用到的 5 个有效位,决定了实际的值(如图 所示)。
在这里插入图片描述
负值以一种称为二补数(或补码)的二进制编码存储。一个数值的二补数通过如下 3 个步骤计算
得到:

  • (1) 确定绝对值的二进制表示(如,对于18,先确定 18 的二进制表示);
  • (2) 找到数值的一补数(或反码),换句话说,就是每个 0 都变成 1,每个 1 都变成 0;
  • (3) 给结果加 1。

基于上述步骤确定18 的二进制表示,首先从 18 的二进制表示开始:
0000 0000 0000 0000 0000 0000 0001 0010
然后,计算一补数,即反转每一位的二进制值:
1111 1111 1111 1111 1111 1111 1110 1101
最后,给一补数加 1:
1111 1111 1111 1111 1111 1111 1110 1101
1
------------------------------------------------------

1111 1111 1111 1111 1111 1111 1110 1110
那么,18 的二进制表示就是 11111111111111111111111111101110。要注意的是,在处理有符号整数
时,我们无法访问第 31 位。

ECMAScript 会帮我们记录这些信息。在把负值输出为一个二进制字符串时,我们会得到一个前面
加了减号的绝对值,如下所示:

let num = -18; 
console.log(num.toString(2)); // "-10010"

在这里插入图片描述

在将18 转换为二进制字符串时,结果得到10010。转换过程会求得二补数,然后再以更符合逻辑
的形式表示出来。
注意: 默认情况下,ECMAScript 中的所有整数都表示为有符号数。不过,确实存在无符 号整数。对无符号整数来说,第 32 位不表示符号,因为只有正值。无符号整数比有符号 整数的范围更大,因为符号位被用来表示数值了。
在对 ECMAScript 中的数值应用位操作符时,后台会发生转换:64 位数值会转换为 32 位数值,然
后执行位操作,最后再把结果从 32 位转换为 64 位存储起来。整个过程就像处理 32 位数值一样,这让二进制操作变得与其他语言中类似。但这个转换也导致了一个奇特的副作用,即特殊值NaN 和Infinity
在位操作中都会被当成 0 处理。
如果将位操作符应用到非数值,那么首先会使用 Number()函数将该值转换为数值(这个过程是自
动的),然后再应用位操作。最终结果是数值。

1. 按位非

按位非操作符用波浪符(~)表示,它的作用是返回数值的一补数。按位非是 ECMAScript 中为数
不多的几个二进制数学操作符之一。看下面的例子:

let num1 = 25; // 二进制 00000000000000000000000000011001 
let num2 = ~num1; // 二进制 11111111111111111111111111100110 
console.log(num2); // -26 

在这里插入图片描述

这里,按位非操作符作用到了数值 25,得到的结果是26。由此可以看出,按位非的最终效果是对
数值取反并减 1,就像执行如下操作的结果一样:

let num1 = 25; 
let num2 = -num1 - 1; 
console.log(num2); // "-26" 

在这里插入图片描述

实际上,尽管两者返回的结果一样,但位操作的速度快得多。这是因为位操作是在数值的底层表示
上完成的。

2. 按位与

按位与操作符用和号(&)表示,有两个操作数。本质上,按位与就是将两个数的每一个位对齐,
然后基于真值表中的规则,对每一位执行相应的与操作。
在这里插入图片描述
按位与操作在两个位都是 1 时返回 1,在任何一位是 0 时返回 0。
下面看一个例子,我们对数值 25 和 3 求与操作,如下所示:

let result = 25 & 3; 
console.log(result); // 1

在这里插入图片描述

25 和 3 的按位与操作的结果是 1。为什么呢?看下面的二进制计算过程:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001
如上所示,25 和 3 的二进制表示中,只有第 0 位上的两个数都是 1。于是结果数值的所有其他位都
会以 0 填充,因此结果就是 1。

3. 按位或

按位或操作符用管道符(|)表示,同样有两个操作数。按位或遵循如下真值表:
在这里插入图片描述
按位或操作在至少一位是 1 时返回 1,两位都是 0 时返回 0。
仍然用按位与的示例,如果对 25 和 3 执行按位或,代码如下所示:

let result = 25 | 3; 
console.log(result); // 27 

在这里插入图片描述

可见 25 和 3 的按位或操作的结果是 27:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
-----------------------------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011
在参与计算的两个数中,有 4 位都是 1,因此它们直接对应到结果上。二进制码 11011 等于 27。

4. 按位异或

按位异或用脱字符(^)表示,同样有两个操作数。下面是按位异或的真值表:
在这里插入图片描述
按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)。
对数值 25 和 3 执行按位异或操作:

let result = 25 ^ 3; 
console.log(result); // 26

在这里插入图片描述

可见,25 和 3 的按位异或操作结果为 26,如下所示:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
两个数在 4 位上都是 1,但两个数的第 0 位都是 1,因此那一位在结果中就变成了 0。其余位上的 1
在另一个数上没有对应的 1,因此会直接传递到结果中。二进制码 11010 等于 26。(注意,这比对同样
两个值执行按位或操作得到的结果小 1。)

5. 左移

左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动。比如,如果数
值 2(二进制 10)向左移 5 位,就会得到 64(二进制 1000000),如下所示:

let oldValue = 2; // 等于二进制 10 
let newValue = oldValue << 5; // 等于二进制 1000000,即十进制 64
console.log(newValue )

在这里插入图片描述

注意在移位后,数值右端会空出 5 位。左移会以 0 填充这些空位,让结果是完整的 32 位数值
在这里插入图片描述

注意,左移会保留它所操作数值的符号。比如,如果2 左移 5 位,将得到64,而不是正 64。

6. 有符号右移

有符号右移由两个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。
有符号右移实际上是左移的逆运算。比如,如果将 64 右移 5 位,那就是 2:

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2 
console.log(newValue )

在这里插入图片描述

同样,移位后就会出现空位。不过,右移后空位会出现在左侧,且在符号位之后如图。
ECMAScript 会用符号位的值来填充这些空位,以得到完整的数值。
在这里插入图片描述

7. 无符号右移

无符号右移用 3 个大于号表示(>>>),会将数值的所有 32 位都向右移。对于正数,无符号右移与
有符号右移结果相同。仍然以前面有符号右移的例子为例,64 向右移动 5 位,会变成 2:

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >>> 5; // 等于二进制 10,即十进制 2 
console.log(newValue )

在这里插入图片描述

对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补 0,而不管符号位是
什么。对正数来说,这跟有符号右移效果相同。但对负数来说,结果就差太多了。无符号右移操作符将
负数的二进制表示当成正数的二进制表示来处理。因为负数是其绝对值的二补数,所以右移之后结果变
得非常之大,如下面的例子所示:

let oldValue = -64; // 等于二进制 11111111111111111111111111000000 
let newValue = oldValue >>> 5; // 等于十进制 134217726
console.log(newValue )

在这里插入图片描述

在对-64 无符号右移 5 位后,结果是 134 217 726。这是因为-64 的二进制表示是 1111111111111111111
1111111000000,无符号右移却将它当成正值,也就是 4 294 967 232。把这个值右移 5 位后,结果是
00000111111111111111111111111110,即 134 217 726。

四、布尔操作符

对于编程语言来说,布尔操作符跟相等操作符几乎同样重要。如果没有能力测试两个值的关系,那
么像 if-else 和循环这样的语句也没什么用了。布尔操作符一共有 3 个:逻辑非、逻辑与和逻辑或。

1. 逻辑非

逻辑非操作符由一个叹号(!)表示,可应用给 ECMAScript 中的任何值。这个操作符始终返回布
尔值,无论应用到的是什么数据类型。逻辑非操作符首先将操作数转换为布尔值,然后再对其取反。换
句话说,逻辑非操作符会遵循如下规则。
 如果操作数是对象,则返回false
 如果操作数是空字符串,则返回true
 如果操作数是非空字符串,则返回 false
 如果操作数是数值 0,则返回 true
 如果操作数是非 0 数值(包括 Infinity),则返回false
 如果操作数是null,则返回 true
 如果操作数是 NaN,则返回 true
 如果操作数是 undefined,则返回 true

console.log(!false); // true 
console.log(!"blue"); // false 
console.log(!0); // true 
console.log(!NaN); // true 
console.log(!""); // true 
console.log(!12345); // false

在这里插入图片描述

逻辑非操作符也可以用于把任意值转换为布尔值。同时使用两个叹号(!!),相当于调用了转型函
Boolean()。无论操作数是什么类型,第一个叹号总会返回布尔值。第二个叹号对该布尔值取反,
从而给出变量真正对应的布尔值。结果与对同一个值使用 Boolean()函数是一样的:

console.log(!!"blue"); // true 
console.log(!!0); // false 
console.log(!!NaN); // false 
console.log(!!""); // false 
console.log(!!12345); // true

在这里插入图片描述

2. 逻辑与

逻辑与操作符由两个和号(&&)表示,应用到两个值,如下所示:

let result = true && false;

在这里插入图片描述

逻辑与操作符遵循如下真值表:
在这里插入图片描述
逻辑与操作符可用于任何类型的操作数,不限于布尔值。如果有操作数不是布尔值,则逻辑与并不
一定会返回布尔值,而是遵循如下规则。
 如果第一个操作数是对象,则返回第二个操作数。
 如果第二个操作数是对象,则只有第一个操作数求值为 true 才会返回该对象。
 如果两个操作数都是对象,则返回第二个操作数。
 如果有一个操作数是 null,则返回 null。
 如果有一个操作数是 NaN,则返回 NaN。  如果有一个操作数是 undefined,则返回 undefined。

逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个
操作数求值。对逻辑与操作符来说,如果第一个操作数是 false,那么无论第二个操作数是什么值,结
果也不可能等于 true。看下面的例子:

let found = true; 
let result = (found && someUndeclaredVariable); // 这里会出错
console.log(result); // 不会执行这一行

在这里插入图片描述

上面的代码之所以会出错,是因为 someUndeclaredVariable 没有事先声明,所以当逻辑与操作符
对它求值时就会报错。变量found 的值是 true,逻辑与操作符会继续求值变量 someUndeclaredVariable
但是由于someUndeclaredVariable没有定义,不能对它应用逻辑与操作符,因此就报错了。假如变
found 的值是 false,那么就不会报错了:

let found = false; 
let result = (found && someUndeclaredVariable); // 不会出错
console.log(result); // 会执行

在这里插入图片描述

这里console.log 会成功执行。即使变量 someUndeclaredVariable 没有定义,由于第一个
操作数是false,逻辑与操作符也不会对它求值,因为此时对&&右边的操作数求值是没有意义的。在使
用逻辑与操作符时,一定别忘了它的这个短路的特性。

3. 逻辑或

逻辑或操作符由两个管道符(||)表示,比如:

let result = true || false;

逻辑或操作符遵循如下真值表:
在这里插入图片描述
与逻辑与类似,如果有一个操作数不是布尔值,那么逻辑或操作符也不一定返回布尔值。它遵循如
下规则。
 如果第一个操作数是对象,则返回第一个操作数。
 如果第一个操作数求值为 false,则返回第二个操作数。
 如果两个操作数都是对象,则返回第一个操作数。
 如果两个操作数都是 null,则返回 null。
 如果两个操作数都是 NaN,则返回 NaN。  如果两个操作数都是 undefined,则返回 undefined。

同样与逻辑与类似,逻辑或操作符也具有短路的特性。只不过对逻辑或而言,第一个操作数求值为
true,第二个操作数就不会再被求值了。看下面的例子:

let found = true; 
let result = (found || someUndeclaredVariable); // 不会出错
console.log(result); // 会执行

在这里插入图片描述

跟前面的例子一样,变量 someUndeclaredVariable 也没有定义。但是,因为变量 found 的值
为 true,所以逻辑或操作符不会对变量 someUndeclaredVariable 求值,而直接返回 true。假如把
found 的值改为 false,那就会报错了:

let found = false; 
let result = (found || someUndeclaredVariable); // 这里会出错
console.log(result); // 不会执行这一行

在这里插入图片描述

利用这个行为,可以避免给变量赋值 null 或 undefined。比如:

 let myObject = preferredObject || backupObject;

在这个例子中,变量 myObject会被赋予两个值中的一个。其中,preferredObject 变量包含首
选的值,backupObject变量包含备用的值。如果preferredObject 不是null,则它的值就会赋给
myObject;如果 preferredObjectnull,则backupObject 的值就会赋给myObject。这种模
式在 ECMAScript 代码中经常用于变量赋值,本书后面的代码示例中也会经常用到。

五、乘性操作符

ECMAScript 定义了 3 个乘性操作符:乘法、除法和取模。这些操作符跟它们在 Java、C 语言及 Perl
中对应的操作符作用一样,但在处理非数值时,它们也会包含一些自动的类型转换。如果乘性操作符有
不是数值的操作数,则该操作数会在后台被使用 Number()转型函数转换为数值。这意味着空字符串会
被当成 0,而布尔值 true 会被当成 1。

1. 乘法操作符

乘法操作符由一个星号(*)表示,可以用于计算两个数值的乘积。其语法类似于 C 语言,比如:

let result = 34 * 56;

不过,乘法操作符在处理特殊值时也有一些特殊的行为。
如果操作数都是数值,则执行常规的乘法运算,即两个正值相乘是正值,两个负值相乘也是正
值,正负符号不同的值相乘得到负值。如果 ECMAScript 不能表示乘积,则返回 Infinity 或
-Infinity。
 如果有任一操作数是 NaN,则返回 NaN
 如果是 Infinity 乘以 0,则返回 NaN。
 如果是 Infinity 乘以非 0的有限数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
 如果是 Infinity 乘以 Infinity,则返回 Infinity。
 如果有不是数值的操作数,则先在后台用 Number()将其转换为数值,然后再应用上述规则。

2. 除法操作符

除法操作符由一个斜杠(/)表示,用于计算第一个操作数除以第二个操作数的商,比如:

let result = 66 / 11; 

跟乘法操作符一样,除法操作符针对特殊值也有一些特殊的行为。
 如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正
值,符号不同的值相除得到负值。如果ECMAScript不能表示商,则返回Infinity或-Infinity。
 如果有任一操作数是 NaN,则返回 NaN。
 如果是 Infinity 除以 Infinity,则返回 NaN。
 如果是 0 除以 0,则返回 NaN。
 如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或-Infinity。
 如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。

3. 取模操作符

取模(余数)操作符由一个百分比符号(%)表示,比如:

let result = 26 % 5; // 等于 1 

与其他乘性操作符一样,取模操作符对特殊值也有一些特殊的行为。
 如果操作数是数值,则执行常规除法运算,返回余数。
 如果被除数是无限值,除数是有限值,则返回 NaN。
 如果被除数是有限值,除数是 0,则返回 NaN。
 如果是 Infinity 除以 Infinity,则返回 NaN。
 如果被除数是有限值,除数是无限值,则返回被除数。
 如果被除数是 0,除数不是 0,则返回 0。
 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。

六、指数操作符(**)

ECMAScript 7 新增了指数操作符,Math.pow()现在有了自己的操作符**,结果是一样的:

console.log(Math.pow(3, 2)); // 9 
console.log(3 ** 2); // 9 
console.log(Math.pow(16, 0.5)); // 4 
console.log(16** 0.5); // 4

在这里插入图片描述

不仅如此,指数操作符也有自己的指数赋值操作符**=,该操作符执行指数运算和结果的赋值操作:

let squared = 3; 
squared **= 2; 
console.log(squared); // 9
let sqrt = 16; 
sqrt **= 0.5; 
console.log(sqrt); // 4

在这里插入图片描述

七、加性操作符

加性操作符,即加法和减法操作符,一般都是编程语言中最简单的操作符。不过,在 ECMAScript
中,这两个操作符拥有一些特殊的行为。与乘性操作符类似,加性操作符在后台会发生不同数据类型的
转换。只不过对这两个操作符来说,转换规则不是那么直观。

1. 加法操作符

加法操作符(+)用于求两个数的和,比如:

let result = 1 + 2; 
  • 如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:
     如果有任一操作数是 NaN,则返回 NaN;
     如果是 Infinity 加 Infinity,则返回 Infinity;
     如果是-Infinity 加-Infinity,则返回-Infinity;
     如果是 Infinity 加-Infinity,则返回 NaN;
     如果是+0 加+0,则返回+0;
     如果是-0 加+0,则返回+0;
     如果是-0 加-0,则返回-0。
  • 不过,如果有一个操作数是字符串,则要应用如下规则:
     如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面;
     如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。
    如果有任一操作数是对象、数值或布尔值,则调用它们的 toString()方法以获取字符串,然后再
    应用前面的关于字符串的规则。对于 undefined 和 null,则调用 String()函数,分别获取
    “undefined"和"null”。

看下面的例子:

let result1 = 5 + 5; // 两个数值
console.log(result1); // 10 
let result2 = 5 + "5"; // 一个数值和一个字符串
console.log(result2); // "55"

在这里插入图片描述

以上代码展示了加法操作符的两种运算模式。正常情况下,5 + 5 等于 10(数值),如前两行代码
所示。但是,如果将一个操作数改为字符串,比如"5",则相加的结果就变成了"55"(原始字符串值),
因为第一个操作数也会被转换为字符串。

ECMAScript 中最常犯的一个错误,就是忽略加法操作中涉及的数据类型。比如下面这个例子:

let num1 = 5; 
let num2 = 10; 
let message = "The sum of 5 and 10 is " + num1 + num2; 
console.log(message); // "The sum of 5 and 10 is 510"

在这里插入图片描述

这里,变量 message 中保存的是一个字符串,是执行两次加法操作之后的结果。有人可能会认为
最终得到的字符串是"The sum of 5 and 10 is 15"。可是,实际上得到的是"The sum of 5 and 10 is 510"。这是因为每次加法运算都是独立完成的。第一次加法的操作数是一个字符串和一个数值(5),
结果还是一个字符串。第二次加法仍然是用一个字符串去加一个数值(10),同样也会得到一个字符串。

** 如果想真正执行数学计算,然后把结果追加到字符串末尾 **,只要使用一对括号即可:

let num2 = 10; 
let message = "The sum of 5 and 10 is " + (num1 + num2); 
console.log(message); // "The sum of 5 and 10 is 15"

在这里插入图片描述

在此,我们用括号把两个数值变量括了起来,意思是让解释器先执行两个数值的加法,然后再把结
果追加给字符串。因此,最终得到的字符串变成了"The sum of 5 and 10 is 15"。

2. 减法操作符

减法操作符(-)也是使用很频繁的一种操作符,比如:

let result = 2 - 1; 

与加法操作符一样,减法操作符也有一组规则用于处理 ECMAScript 中不同类型之间的转换。
 如果两个操作数都是数值,则执行数学减法运算并返回结果。
 如果有任一操作数是 NaN,则返回 NaN。
 如果是 Infinity 减 Infinity,则返回 NaN。
 如果是-Infinity 减-Infinity,则返回 NaN。
如果是 Infinity 减-Infinity,则返回 Infinity。
 如果是-Infinity 减 Infinity,则返回-Infinity。  如果是+0 减+0,则返回+0。  如果是+0 减-0,则返回-0。
 如果是-0 减-0,则返回+0。  如果有任一操作数是字符串、布尔值、null 或 undefined,则先在后台使用 Number()将其转换为数值,然后再根据前面的规则执行数学运算。如果转换结果是 NaN,则减法计算的结果是NaN
 如果有任一操作数是对象,则调用其 valueOf()方法取得表示它的数值。如果该值是 NaN,则
减法计算的结果是 NaN。如果对象没有 valueOf()方法,则调用其 toString()方法,然后再
将得到的字符串转换为数值。

以下示例演示了上面的规则:

let result1 = 5 - true; // true 被转换为 1,所以结果是 4
let result2 = NaN - 1; // NaN
let result3 = 5 - 3; // 2
let result4 = 5 - ""; // ""被转换为 0,所以结果是 5
let result5 = 5 - "2"; // "2"被转换为 2,所以结果是 3
let result6 = 5 - null; // null 被转换为 0,所以结果是 5
console.log(result1)
console.log(result2)
console.log(result3)
console.log(result4)
console.log(result5)
console.log(result6)

在这里插入图片描述

八、关系操作符

关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=)
用法跟数学课上学的一样。这几个操作符都返回布尔值,如下所示:

let result1 = 5 > 3; // true 
let result2 = 5 < 3; // false
console.log(result1)
console.log(result2)

在这里插入图片描述

与 ECMAScript 中的其他操作符一样,在将它们应用到不同数据类型时也会发生类型转换和其他行为。
 如果操作数都是数值,则执行数值比较。
 如果操作数都是字符串,则逐个比较字符串中对应字符的编码。
 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较。
 如果有任一操作数是对象,则调用其 valueOf()方法,取得结果后再根据前面的规则执行比较。
如果没有 valueOf()操作符,则调用 toString()方法,取得结果后再根据前面的规则执行比较。
 如果有任一操作数是布尔值,则将其转换为数值再执行比较。
在使用关系操作符比较两个字符串时,会发生一个有趣的现象。很多人认为小于意味着“字母顺序
靠前”,而大于意味着“字母顺序靠后”,实际上不是这么回事。对字符串而言,关系操作符会比较字符
串中对应字符的编码,而这些编码是数值。比较完之后,会返回布尔值。问题的关键在于,大写字母的
编码都小于小写字母的编码,因此以下这种情况就会发生:

let result = "Brick" < "alphabet"; // true

在这里,字符串"Brick"被认为小于字符串"alphabet",因为字母 B 的编码是 66,字母 a 的编码
是 97。要得到确实按字母顺序比较的结果,就必须把两者都转换为相同的大小写形式(全大写或全小写),
然后再比较:

let result = "Brick".toLowerCase() < "alphabet".toLowerCase(); // false

将两个操作数都转换为小写,就能保证按照字母表顺序判定"alphabet"在"Brick"前头。
另一个奇怪的现象是在比较两个数值字符串的时候,比如下面这个例子:

let result = "23" < "3"; // true

这里在比较字符串"23"和"3"时返回 true。因为两个操作数都是字符串,所以会逐个比较它们的
字符编码(字符"2"的编码是 50,而字符"3"的编码是 51)。不过,如果有一个操作数是数值,那么比
较的结果就对了:

let result = "23" < 3; // false 

因为这次会将字符串"23"转换为数值 23,然后再跟 3 比较,结果当然对了。只要是数值和字符串
比较,字符串就会先被转换为数值,然后进行数值比较。对于数值字符串而言,这样能保证结果正确。
但如果字符串不能转换成数值呢?比如下面这个例子:

let result = "a" < 3; // 因为"a"会转换为 NaN,所以结果是 false 

因为字符"a"不能转换成任何有意义的数值,所以只能转换为 NaN。这里有一个规则,即任何关系
操作符在涉及比较 NaN 时都返回 false。这样一来,下面的例子有趣了:

let result1 = NaN < 3; // false 
let result2 = NaN >= 3; // false 

在大多数比较的场景中,如果一个值不小于另一个值,那就一定大于或等于它。但在比较 NaN 时,
无论是小于还是大于等于,比较的结果都会返回 false。

九、相等操作符

判断两个变量是否相等是编程中最重要的操作之一。在比较字符串、数值和布尔值是否相等时,过
程都很直观。但是在比较两个对象是否相等时,情形就比较复杂了。ECMAScript 中的相等和不相等操
作符,原本在比较之前会执行类型转换,但很快就有人质疑这种转换是否应该发生。最终,ECMAScript提供了两组操作符。第一组是等于和不等于,它们在比较之前执行转换。第二组是全等和不全等,它们
在比较之前不执行转换。

1. 等于和不等于

ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。不等于
操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。这两个操作符都会先进
行类型转换(通常称为强制类型转换)再确定操作数是否相等。

  • 在转换操作数的类型时,相等和不相等操作符遵循如下规则。
     如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换
    为 1。
     如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否
    相等。
     如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法取得其原始值,再
    根据前面的规则进行比较。
  • 在进行比较时,这两个操作符会遵循如下规则。
     null 和 undefined 相等。
     null 和 undefined 不能转换为其他类型的值再进行比较。
     如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两
    个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN。
     如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,
    则相等操作符返回 true。否则,两者不相等。
    下表总结了一些特殊情况及比较的结果。
    在这里插入图片描述
2. 全等和不全等

全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。全等操
作符由 3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回true,比如:

let result1 = ("55" == 55); // true,转换后相等
let result2 = ("55" === 55); // false,不相等,因为数据类型不同

在这个例子中,第一个比较使用相等操作符,比较的是字符串"55"和数值 55。如前所述,因为字符串"55"会被转换为数值 55,然后再与数值 55 进行比较,所以返回 true。第二个比较使用全等操作
符,因为没有转换,字符串和数值当然不能相等,所以返回 false。
不全等操作符用一个叹号和两个等于号(!==)表示,只有两个操作数在不转换的前提下不相等才
返回 true。比如:

let result1 = ("55" != 55); // false,转换后相等
let result2 = ("55" !== 55); // true,不相等,因为数据类型不同

这一次,第一个比较使用不相等操作符,它会把字符串"55"转换为数值 55,跟第二个操作数相等。
既然转换后两个值相等,那就返回 false。第二个比较使用不全等操作符。这时候可以这么问:“字符
串 55 和数值 55 有区别吗?”答案是“有”(true)。
另外,虽然 null == undefined 是 true(因为这两个值类似),但 null === undefined 是
false,因为它们不是相同的数据类型。

注意 由于相等和不相等操作符存在类型转换问题,因此推荐使用全等和不全等操作符。 这样有助于在代码中保持数据类型的完整性。

十、条件操作符

条件操作符是 ECMAScript 中用途最为广泛的操作符之一,语法跟 Java 中一样:

let variable = boolean_expression ? true_value : false_value; 

上面的代码执行了条件赋值操作,即根据条件表达式 boolean_expression 的值决定将哪个值赋
给变量 variable 。如果 boolean_expression 是 true ,则赋值 true_value ;如果
boolean_expression 是 false,则赋值 false_value。比如:

let max = (num1 > num2) ? num1 : num2; 

在这个例子中,max 将被赋予一个最大值。这个表达式的意思是,如果 num1 大于 num2(条件表
达式为 true),则将 num1 赋给 max。否则,将 num2 赋给 max。

十、赋值操作符

简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量,如下所示:

let num = 10; 

复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。这些赋值操作符是类似如下常见赋值
操作的简写形式:

let num = 10; 
num = num + 10; 

以上代码的第二行可以通过复合赋值来完成:

let num = 10; 
num += 10; 

每个数学操作符以及其他一些操作符都有对应的复合赋值操作符
 乘后赋值(*=)
 除后赋值(/=)
 取模后赋值(%=)
 加后赋值(+=)
 减后赋值(-=)
 左移后赋值(<<=)
 右移后赋值(>>=)
 无符号右移后赋值(>>>=)
这些操作符仅仅是简写语法,使用它们不会提升性能。

十一、逗号操作符

逗号操作符可以用来在一条语句中执行多个操作,如下所示:

let num1 = 1, num2 = 2, num3 = 3; 

在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助
赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值:

let num = (5, 1, 4, 8, 0); // num 的值为 0 

在这个例子中,num 将被赋值为 0,因为 0 是表达式中最后一项。逗号操作符的这种使用场景并不
多见,但这种行为的确存在。

十二、结语

学习要抓住基本知识:即不好高骛远,而忽略基本的东西。喜马拉雅山是世界著名的高山,因为它是建立在喜马拉雅山之上,盘基广大高原之上的一个高峰;假如把喜马拉雅山建立在河海平原上,八千公尺的高峰是难以存在的,犹如无源之水易于枯竭的。——徐特立
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值