转行学前端的第 28 天 : 了解 ECMAScript 操作符

2,080 阅读29分钟

我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。

今日学习目标

昨天基于一些页面搜索,学习了《JavaScirpt 高级程序设计》(第三版)  第 3 章节中的 ES 基础概念中 String 数据类型的很多方法和属性,发现其实还是有很多基础知识点需要补充学习。 所以,今天主要是基于搜索来仔细学习 第 3 章节中的 3.5 操作符,又是适合学习的一天,加油,小又又!!!!


一元操作符

递增操作符

基础说明

递增运算符为其操作数增加1,返回一个数值。

  • 如果使用后置(postfix),即运算符位于操作数的后面(如 x++),那么将会在递增前返回数值。
  • 如果使用前置(prefix),即运算符位于操作数的前面(如 ++x),那么将会在递增后返回数值。

语法

运算符: x++ 或者 ++x

示例

// 后置 
var x = 3;
y = x++; 
// y = 3, x = 4

// 前置
var a = 2;
b = ++a; 
// a = 3, b = 3

注意事项

递增运算符不仅应用于数字值,还可以用于字符串、布尔值、浮点值和对象,在应用于不同的值时,递增操作符会遵循下列规则。

  • 在应用于一个包含有效数字字符的字符串时,会先将其转换为数字值,将字符串变量变成数值变量,再执行加一的操作。
  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN,将字符串变量变成数值变量。
  • 在应用于布尔值 false 时,先将其转换为 0,将布尔值变量变成数值变量 ,再执行加一的操作。
  • 在应用于布尔值 true 时,先将其转换为 1,将布尔值变量变成数值变量 ,再执行加一的操作。
  • 在应用于浮点数值时,执行加一的操作。
  • 在应用于对象时,先调用对象的 valueOf() 方法,以取得一个可供操作的值。然后对该值应用前述规则,如果转换之后的值是 NaN ,则再调用 toString() 方法后,再应用前述规则,将对象变量变成数值变量。
var s1 = "2";
var s2 = "z";
var b1 = false;
var b2 = true;
var f = 1.1;
var o = {
 valueOf: function(){
  return -1;
 }
};

s1++;//  3
s2++;// NaN
b1++;// 1
b2++;// 2
f++;// 2.1
o++;// 0

递减操作符

基础说明

递减运算符将其操作数减去1,并返回一个数值。

  • 如果后置使用(如 x--),则在递减前返回数值。
  • 如果前置使用(如 --x),则在递减后返回数值。

语法

运算符: x-- or --x

示例

// 后置 
var x = 3;
y = x--; // y = 3, x = 2

// 前置
var a = 2;
b = --a; // a = 1, b = 1

注意事项

递减运算符不仅应用于数字值,还可以用于字符串、布尔值、浮点值和对象,在应用于不同的值时,和上面递增操作符会遵循同样规则。

var s1 = "2";
var s2 = "z";
var b1 = false;
var b2 = true;
var f = 1.1;
var o = {
 valueOf: function(){
  return -1;
 }
};

s1--;//  1
s2--;// NaN
b1--;// -2
b2--;// -1
f--;// 0.10000000000000009
o--;// -2

一元加和减操作符

基础说明

绝大多数开发人员对一元加减都不会陌生,而且这两个 ECMAScript 操作符的作用与数学书上讲的完全一样。 主要是在具体数值前添加 + 或者 - 操作符。

  • 加号(+)放在数值前面,对数值不会产生任何影响。
  • 减号(-)放在数值前面,表示数值是一个负数。

主要用于基础的算术运算,也会应用于数据类型转换。


语法

运算符: x-- or --x

示例

var s1 = "2";
var s2 = "z";
var b1 = false;
var b2 = true;
var f = 1.1;
var o = {
 valueOf: function(){
  return -1;
 }
};

s1 = +s1;//  2
s2 = -s2;// NaN
b1 = +b1;// 0
b2 = -b2;// -1
f = +f;// 1.1
o = -o;// 1

注意事项

一元正号运算符位于其操作数前面,计算其操作数的数值,如果操作数不是一个数值,会尝试将其转换成一个数值。

尽管一元负号也能转换非数值类型,但是一元正号是转换其他对象到数值的最快方法,也是最推荐的做法,因为它不会对数值执行任何多余操作。它可以将字符串转换成整数和浮点数形式,也可以转换非字符串值 truefalse  null

小数和十六进制格式字符串也可以转换成数值。负数形式字符串也可以转换成数值(对于十六进制不适用)。如果它不能解析一个值,则计算结果为 NaN


位操作符

基础说明

按位操作符(Bitwise operators) 将其操作数(operands)当作32位的比特序列(由0和1组成),而不是十进制、十六进制或八进制数值

例如,十进制数9,用二进制表示则为1001。按位操作符操作数字的二进制形式,但是返回值依然是标准的JavaScript数值。

运算符 用法 描述
按位与( AND) a & b 对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。
按位或(OR) a | b 对于每一个比特位,当两个操作数相应的比特位至少有一个1时,结果为1,否则为0。
按位异或(XOR) a ^ b 对于每一个比特位,当两个操作数相应的比特位有且只有一个1时,结果为1,否则为0。
按位非(NOT) ~ a 反转操作数的比特位,即0变成1,1变成0。
左移(L) a << b a 的二进制形式向左移 b (< 32) 比特位,右边用0填充。
有符号右移 a >> b 将 a 的二进制表示向右移b(< 32) 位,丢弃被移出的位。
无符号右移 a >>> b 将 a 的二进制表示向右移b(< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。

有符号32位整数

所有的按位操作符的操作数都会被转成补码(two's complement)形式的有符号32位整数。 补码形式是指一个数的负对应值(negative counterpart)(如 5和-5)为数值的所有比特位反转后,再加1。反转比特位即该数值进行位运算,也即该数值的反码。

补码保证了当一个数是正数时,其最左的比特位是0,当一个数是负数时,其最左的比特位是1。因此,最左边的比特位被称为符号位(sign bit)。

0 是所有比特数字0组成的整数。

0 (base 10) = 00000000000000000000000000000000 (base 2)

-1 是所有比特数字1组成的整数。

-1 (base 10) = 11111111111111111111111111111111 (base 2)

从概念上讲,按位逻辑操作符按遵守下面规则

  • 操作数被转换成32位整数,用比特序列(0和1组成)表示。超过32位的数字会被丢弃。例如, 以下具有32位以上的整数将转换为32位整数
转换前: 11100110111110100000000000000110000000000001
转换后:             10100000000000000110000000000001
  • 第一个操作数的每个比特位与第二个操作数的相应比特位匹配:第一位对应第一位,第二位对应第二位,以此类推。
  • 位运算符应用到每对比特位,结果是新的比特值。

按位非(NOT)

对每一个比特位执行非(NOT)操作。NOT a 结果为 a 的反转(即反码)。

非操作的真值表

a NOT a
0 1
1 0
9 (base 10) = 00000000000000000000000000001001 (base 2)
               --------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)

对任一数值 x 进行按位非操作的结果为  -(x + 1)。例如,~5 结果为 -6。

与 indexOf 一起使用示例

var str = 'rawr';
var searchFor = 'a';
// 这是 if (-1*str.indexOf('a') <= 0) 条件判断的另一种方法
if (~str.indexOf(searchFor)) {
  // searchFor 包含在字符串中
} else {
  // searchFor 不包含在字符串中
}
// (~str.indexOf(searchFor))的返回值
// r == -1
// a == -2
// w == -3

按位与(AND)

对每对比特位执行与(AND)操作。只有 a 和 b 都是 1 时,a AND b 才是 1。与操作的真值表如下

a b a AND b
0 0 0
0 1 0
1 0 0
1 1 1
9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 & 9 (base 10) = 00000000000000000000000000001000 (base 2) = 8 (base 10)

将任一数值 x 与 0 执行按位与操作,其结果都为 0。将任一数值 x 与 -1 执行按位与操作,其结果都为 x。


按位或(OR)

对每一对比特位执行或(OR)操作。如果 a 或 b 为 1,则 a OR b 结果为 1。

或操作的真值表:

a b a OR b
0 0 0
0 1 1
1 0 1
1 1 1
9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)

将任一数值 x 与 0 进行按位或操作,其结果都是 x。将任一数值 x 与 -1 进行按位或操作,其结果都为 -1。 补充一些例子

1 | 0 ;                       // 1
1.1 | 0 ;                     // 1
'asfdasfda' | 0 ;             // 0
0 | 0 ;                       // 0
(-1) | 0 ;                    // -1
(-1.5646) | 0 ;               // -1
[] | 0 ;                      // 0
({}) | 0 ;                    // 0
"123456" | 0 ;            // 123456
1.23E2 | 0;               // 123
1.23E12 | 0;              // 1639353344
-1.23E2 | 0;              // -123
-1.23E12 | 0;             // -1639353344

按位异或(XOR)

对每一对比特位执行异或(XOR)操作。当 a 和 b 不相同时,a XOR b 的结果为 1。异或操作真值表:

a b a XOR b
0 0 0
0 1 1
1 0 1
1 1 0
9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)

将任一数值 x 与 0 进行异或操作,其结果为 x。将任一数值 x 与 -1 进行异或操作,其结果为 ~x。


左移

该操作符会将第一个操作数向左移动指定的位数。向左被移出的位被丢弃,右侧用 0 补充。 举例, 9 << 2 yields 36

9 (base 10): 00000000000000000000000000001001 (base 2)
                  --------------------------------
9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)

在数字 x 上左移 y 比特得到 x * 2.


有符号的右移

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,拷贝最左侧的位以填充左侧。 由于新的最左侧的位总是和以前相同,符号位没有被改变。所以被称作“符号传播”。 例如, 9 >> 2 得到 2

9 (base 10): 00000000000000000000000000001001 (base 2)
                  --------------------------------
9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

相比之下, -9 >> 2 得到 -3,因为符号被保留了。

     -9 (base 10): 11111111111111111111111111110111 (base 2)
                   --------------------------------
-9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)

无符号的右移

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,左侧用0填充。 因为符号位变成了 0,所以结果总是非负的。(译注:即便右移 0 个比特,结果也是非负的。) 对于非负数,有符号右移和无符号右移总是返回相同的结果。 例如 9 >>> 29 >> 2 一样返回 2

9 (base 10): 00000000000000000000000000001001 (base 2)
                   --------------------------------
9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

但是对于负数却不尽相同。 -9 >>> 2 产生 1073741821 这和 -9 >> 2 不同

-9 (base 10): 11111111111111111111111111110111 (base 2)
                    --------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)

综合案例

例子:标志位与掩码

位运算经常被用来创建、处理以及读取标志位序列——一种类似二进制的变量。虽然可以使用变量代替标志位序列,但是这样可以节省内存(1/32)。 例如,有 4 个标志位

  • 标志位 A:我们有 ant
  • 标志位 B:我们有 bat
  • 标志位 C:我们有 cat
  • 标志位 D:我们有 duck

标志位通过位序列 DCBA 来表示。

  • 当一个位被置位 (set) 时,它的值为 1 。
  • 当被清除 (clear) 时,它的值为 0 。

例如一个变量 flags 的二进制值为 0101

var flags = 5;   // 二进制 0101

这个值表示:

  • 标志位 A 是 true (我们有 ant);
  • 标志位 B 是 false (我们没有 bat);
  • 标志位 C 是 true (我们有 cat);
  • 标志位 D 是 false (我们没有 duck);

因为位运算是 32 位的, 0101 实际上是 00000000000000000000000000000101。因为前面多余的 0 没有任何意义,所以他们可以被忽略。


**掩码 (bitmask) **是一个通过与/或来读取标志位的位序列。 典型的定义每个标志位的原语掩码如下

var FLAG_A = 1; // 0001
var FLAG_B = 2; // 0010
var FLAG_C = 4; // 0100
var FLAG_D = 8; // 1000

新的掩码可以在以上掩码上使用逻辑运算创建。 例如,掩码 1011 可以通过 FLAG_A、FLAG_B 和 FLAG_D 逻辑或得到:

var mask = FLAG_A | FLAG_B | FLAG_D; // 0001 | 0010 | 1000 => 1011

某个特定的位可以通过与掩码做逻辑与运算得到,通过与掩码的与运算可以去掉无关的位,得到特定的位。 例如,掩码 0100 可以用来检查标志位 C 是否被置位

// 如果我们有 cat
if (flags & FLAG_C) { // 0101 & 0100 => 0100 => true
   // do stuff
}

一个有多个位被置位的掩码表达任一/或者的含义。 例如,以下两个表达是等价的

// 如果我们有 bat 或者 cat 至少一个
// (0101 & 0010) || (0101 & 0100) => 0000 || 0100 => true
if ((flags & FLAG_B) || (flags & FLAG_C)) {
   // do stuff
}
// 如果我们有 bat 或者 cat 至少一个
var mask = FLAG_B | FLAG_C; // 0010 | 0100 => 0110
if (flags & mask) { // 0101 & 0110 => 0100 => true
   // do stuff
}

可以通过与掩码做或运算设置标志位,掩码中为 1 的位可以设置对应的位。 例如掩码 1100 可用来设置位 C 和 D

// 我们有 cat 和 duck
var mask = FLAG_C | FLAG_D; // 0100 | 1000 => 1100
flags |= mask;   // 0101 | 1100 => 1101

可以通过与掩码做与运算清除标志位,掩码中为 0 的位可以设置对应的位。 掩码可以通过对原语掩码做非运算得到。 例如,掩码 1010 可以用来清除标志位 A 和 C

// 我们没有 ant 也没有 cat
var mask = ~(FLAG_A | FLAG_C); // ~0101 => 1010
flags &= mask;   // 1101 & 1010 => 1000

如上的掩码同样可以通过 ~FLAG_A & ~FLAG_C 得到(德摩根定律)

// 我们没有 ant 也没有 cat
var mask = ~FLAG_A & ~FLAG_C;
flags &= mask;   // 1101 & 1010 => 1000

标志位可以使用异或运算切换。所有值为 1 的位可以切换对应的位。 例如,掩码 0110 可以用来切换标志位 B 和 C:

// 如果我们以前没有 bat ,那么我们现在有 bat
// 但是如果我们已经有了一个,那么现在没有了
// 对 cat 也是相同的情况
var mask = FLAG_B | FLAG_C;
flags = flags ^ mask;   // 1100 ^ 0110 => 1010

最后,所有标志位可以通过非运算翻转

// entering parallel universe...
flags = ~flags;    // ~1010 => 0101

转换片段

将一个二进制数的 [String](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String) 转换为十进制的 [Number](https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Global_Objects/Number):

var sBinString = "1011";
var nMyNumber = parseInt(sBinString, 2);
alert(nMyNumber); // 打印 11

将一个十进制的 Number转换为二进制数的 String

var nMyNumber = 11;
var sBinString = nMyNumber.toString(2);
alert(sBinString); // 打印 1011

自动化掩码创建

如果你需要从一系列的 Boolean 值创建一个掩码,你可以

function createMask () {
  var nMask = 0, nFlag = 0, nLen = arguments.length > 32 ? 32 : arguments.length;
  for (nFlag; nFlag < nLen; nMask |= arguments[nFlag] << nFlag++);
  return nMask;
}
var mask1 = createMask(true, true, false, true); // 11, i.e.: 1011
var mask2 = createMask(false, false, true); // 4, i.e.: 0100
var mask3 = createMask(true); // 1, i.e.: 0001
// etc.
alert(mask1); // 打印 11

逆算法:从掩码得到布尔数组

如果你希望从掩码得到得到 Boolean ,Array

function arrayFromMask (nMask) {
  // nMask 必须介于 -2147483648 和 2147483647 之间
  if (nMask > 0x7fffffff || nMask < -0x80000000) { 
    throw new TypeError("arrayFromMask - out of range"); 
  }
  for (var nShifted = nMask, aFromMask = []; nShifted; 
       aFromMask.push(Boolean(nShifted & 1)), nShifted >>>= 1);
  return aFromMask;
}
var array1 = arrayFromMask(11);
var array2 = arrayFromMask(4);
var array3 = arrayFromMask(1);
alert("[" + array1.join(", ") + "]");
// 打印 "[true, true, false, true]", i.e.: 11, i.e.: 1011

你可以同时测试以上两个算法……

var nTest = 19; // our custom mask
var nResult = createMask.apply(this, arrayFromMask(nTest));
alert(nResult); // 19

仅仅由于教学目的 (因为有 Number.toString(2)方法),我们展示如何修改 arrayFromMask 算法通过 Number 返回二进制的 String,而非 Boolean Array

function createBinaryString (nMask) {
  // nMask must be between -2147483648 and 2147483647
  for (var nFlag = 0, nShifted = nMask, sMask = ""; nFlag < 32;
       nFlag++, sMask += String(nShifted >>> 31), nShifted <<= 1);
  return sMask;
}
var string1 = createBinaryString(11);// 00000000000000000000000000001011
var string2 = createBinaryString(4);// 00000000000000000000000000000100
var string3 = createBinaryString(1);// 00000000000000000000000000000001


布尔操作符

基础描述

逻辑运算符通常用于布尔型(逻辑)值。这种情况下,它们返回一个布尔值。然而,&& 和 || 运算符会返回一个指定操作数的值,因此,这些运算符也用于非布尔值。这时,它们也就会返回一个非布尔型值。

逻辑运算符如下表所示 (其中_expr_可能是任何一种类型, 不一定是布尔值):

运算符 语法 说明
逻辑与,AND(&& _expr1_ && _expr2_ expr**1** 可转换为 true,则返回 expr**2**;否则,返回 expr**1**
逻辑或,OR(|| _expr1_ || _expr2_ expr**1** 可转换为 true,则返回 expr**1**;否则,返回 expr**2**
逻辑非,NOT(! !_expr_ expr 可转换为 true,则返回 false;否则,返回 true

如果一个值可以被转换为 true,那么这个值就是所谓的 truthy,如果可以被转换为 false,那么这个值就是所谓的 falsy。 会被转换为 false 的表达式有:

  • null
  • NaN
  • 0
  • 空字符串("" or '' or ````);
  • undefined

尽管 &&|| 运算符能够使用非布尔值的操作数, 但它们依然可以被看作是布尔操作符,因为它们的返回值总是能够被转换为布尔值。如果要显式地将它们的返回值(或者表达式)转换为布尔值,请使用双重非运算符(即!!)或者 Boolean 构造函数。


逻辑非

下面的代码是 ! (逻辑非) 运算符的示例

n1 = !true              // !t 返回 false
n2 = !false             // !f 返回 true
n3 = !''                // !f 返回 true
n4 = !'Cat'             // !t 返回 false

双重非(!!)运算符

可能使用双重非运算符的一个场景,是显式地将任意值强制转换为其对应的 布尔值。这种转换是基于被转换值的 "truthyness" 和 "falsyness"的。同样的转换可以通过 Boolean 函数完成。

n1 = !!true                   // !!truthy 返回 true
n2 = !!{}                     // !!truthy 返回 true: 任何 对象都是 truthy 的…
n3 = !!(new Boolean(false))   // …甚至 .valueOf() 返回 false 的布尔值对象也是!
n4 = !!false                  // !!falsy 返回 false
n5 = !!""                     // !!falsy 返回 false
n6 = !!Boolean(false)         // !!falsy 返回 false

逻辑与

下面的代码是 && (逻辑与) 运算符的示例

a1 = true  && true      // t && t 返回 true
a2 = true  && false     // t && f 返回 false
a3 = false && true      // f && t 返回 false
a4 = false && (3 == 4)  // f && f 返回 false
a5 = "Cat" && "Dog"     // t && t 返回 "Dog"
a6 = false && "Cat"     // f && t 返回 false
a7 = "Cat" && false     // t && f 返回 false
a8 = ''    && false     // f && f 返回 ""
a9 = false && ''        // f && f 返回 false

逻辑或

下面的代码是 || (逻辑或) 运算符的示例。

o1 = true  || true      // t || t 返回 true
o2 = false || true      // f || t 返回 true
o3 = true  || false     // t || f 返回 true
o4 = false || (3 == 4)  // f || f 返回 false
o5 = "Cat" || "Dog"     // t || t 返回 "Cat"
o6 = false || "Cat"     // f || t 返回 "Cat"
o7 = "Cat" || false     // t || f 返回 "Cat"
o8 = ''    || false     // f || f 返回 false
o9 = false || ''        // f || f 返回 ""

短路计算

由于逻辑表达式的运算顺序是从左到右,也可以用以下规则进行"短路"计算:

  • (some falsy expression) && (_expr)_ 短路计算的结果为假。
  • (some truthy expression) || _(expr)_ 短路计算的结果为真。

短路意味着上述表达式中的expr部分不会被执行,因此expr的任何副作用都不会生效(举个例子,如果expr是一次函数调用,这次调用就不会发生)。造成这种现象的原因是,整个表达式的值在第一个操作数被计算后已经确定了。 看一个例子:

function A(){ console.log('called A'); return false; }
function B(){ console.log('called B'); return true; }
console.log( A() && B() );
// logs "called A" due to the function call,
// then logs false (which is the resulting value of the operator)
console.log( B() || A() );
// logs "called B" due to the function call,
// then logs true (which is the resulting value of the operator)

操作符优先级

请注意,由于运算符优先级的存在,下面的表达式的结果却不相同。右侧被小括号括起来的操作变成了独立的表达式。

false &&  true || true      // 结果为 true
false && (true || true)     // 结果为 false

综合案例

逻辑操作符之间是可以进行相关转换的~~~~

转换方向 转换前 转换后
将 AND 转换为 OR bCondition1 && bCondition2 !(!bCondition1
将 OR 转换为 AND bCondition1 || bCondition2 !(!bCondition1 && !bCondition2)
删除嵌套的 AND bCondition1 || (bCondition2 && bCondition3) bCondition1
删除嵌套的 OR bCondition1 && (bCondition2

乘性操作符

乘法

基础说明

乘法运算符的结果是操作数的乘积。


语法

运算符: x * y

示例

2 * 2 // 4
-2 * 2 // -4
Infinity * 0 // NaN
Infinity * Infinity // Infinity
"foo" * 2 // NaN

除法

基础说明

除法运算符的结果是操作数的商 ,左操作数是被除数,右操作数是除数。


语法

运算符: x / y

示例

1 / 2      // 在 JavaScript 中返回 0.5
1 / 2      // 在 Java 中返回 0
// (不需要数字是明确的浮点数)

1.0 / 2.0  // 在 JavaScript 或 Java 中都返回 0.5

2.0 / 0    // 在 JavaScript 中返回 Infinity
2.0 / 0.0  // 同样返回 Infinity 
2.0 / -0.0 // 在 JavaScript 中返回 -Infinity

求模

基础说明

求余运算符返回第一个操作数对第二个操作数的模,即 var1 对 var2 取模,其中 var1 和 var2 是变量。取模功能就是 var1 除以 var2 的整型余数。


语法

运算符: var1 % var2

示例

12 % 5 // 2
-1 % 2 // -1
NaN % 2 // NaN
1 % 2 // 1
2 % 3 // 2
-4 % 2 // -0
5.5 % 2 // 1.5

基础说明

幂运算符返回第一个操作数做底数,第二个操作数做指数的乘方。即,var1``var2,其中 var1 和 var2 是其两个操作数。幂运算符是右结合的。a ** b ** c 等同于 a ** (b ** c)


语法

运算符: var1 ** var2

示例

2 ** 3 // 8
3 ** 2 // 9
3 ** 2.5 // 15.588457268119896
10 ** -1 // 0.1
NaN ** 2 // NaN

2 ** 3 ** 2 // 512
2 ** (3 ** 2) // 512
(2 ** 3) ** 2 // 64

// 如果要反转求幂表达式结果的符号,你可以采用这样的方式
-(2 ** 2) // -4

// 强制求幂表达式的基数为负数
(-2) ** 2 // 4

注意事项

包括 PHP 或 Python 等的大多数语言中,都包含幂运算符(一般来说符号是 ^ 或者 **)。这些语言中的幂运算符有着比其他的单目运算符(如一元 + 或一元 - )更高的优先级。

但是作为例外,在 Bash 中, **运算符被设计为比单目运算符优先级更低。在最新的 JavaScript(ES2016) 中,禁止使用带歧义的幂运算表达式。

比如,底数前不能紧跟一元运算符(+/-/~/!/delete/void/typeof)。

-2 ** 2; 
// Illegal expression. Wrap left hand side or entire exponentiation in parentheses.
// 在 Bash 中等于 4 ,而在其他语言中一般等于 -4
// 在 JavaScript 中是错误的,因为这会有歧义

-(2 ** 2);
// -4 在 JavaScript 中能够明显体现出作者的意图

加性操作符

加法

基础说明

加法运算符的作用是数值求和,或者字符串拼接。


语法

运算符: x + y

示例

// Number + Number -> 数字相加
1 + 2 // 3

// Boolean + Number -> 数字相加
true + 1 // 2

// Boolean + Boolean -> 数字相加
false + false // 0

// Number + String -> 字符串连接
5 + "foo" // "5foo"

// String + Boolean -> 字符串连接
"foo" + false // "foofalse"

// String + String -> 字符串连接
"foo" + "bar" // "foobar"

减法

基础说明

减法运算符使两个操作数相减,结果是它们的差值。


语法

运算符: x - y

示例

5 - 3 // 2
3 - 5 // -2
"foo" - 3 // NaN

关系操作符

大于运算符 (>)

大于运算符仅当左操作数大于右操作数时返回 true

语法

x > y

例子

4 > 3 // true

大于等于运算符 (>=)

大于等于运算符当左操作数大于或等于右操作数时返回 true

语法

x >= y

例子

4 >= 3 // true
3 >= 3 // true

小于运算符 (<)

小于运算符仅当左操作数小于右操作数时返回true

语法

x < y

例子

3 < 4 // true

小于等于运算符 (<=)

小于等于运算符当左操作数小于或等于右操作数时返回true

语法

x <= y

例子

3 <= 4 // true

等式操作符

相等操作符

基础说明

比较操作符会为两个不同类型的操作数转换类型,然后进行严格比较。

当两个操作数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。

x == y

使用案例

 1   ==  1     // true
"1"  ==  1     // true
 1   == '1'    // true
 0   == false  // true

不相等操作符

基础说明

不等操作符仅当操作数不相等时返回true,如果两操作数不是同一类型,JavaScript会尝试将其转为一个合适的类型,然后进行比较。

如果两操作数为对象类型,JavaScript会比较其内部引用地址,仅当他们在内存中引用不同对象时不相等。

x != y

使用案例

1 !=   2     // true
1 !=  "1"    // false
1 !=  '1'    // false
1 !=  true   // false
0 !=  false  // false

严格相等操作符

基础说明

一致运算符不会进行类型转换,仅当操作数严格相等时返回true

x === y

使用案例

3 === 3   // true
3 === '3' // false
var object1 = {"value":"key"}, object2={"value":"key"};
object1 === object2 //false

严格不相等操作符

基础说明

不一致运算符当操作数不相等或不同类型时返回true

x !== y

使用案例

3 !== '3' // true
4 !== 3   // true

条件操作符

基础说明

条件(三元)运算符是 JavaScript 仅有的使用三个操作数的运算符。一个条件后面会跟一个问号(?),如果条件为 truthy ,则问号后面的表达式A将会执行;表达式A后面跟着一个冒号(:),如果条件为 falsy ,则冒号后面的表达式B将会执行。本运算符经常作为 if 语句的简捷形式来使用。

condition ? exprIfTrue : exprIfFalse

参数

  • condition计算结果用作条件的表达式
  • exprIfTrue如果表达式 condition 的计算结果是 truthy(它和 true 相等或者可以转换成 true ),那么表达式 exprIfTrue 将会被求值。
  • exprIfFalse如果表达式 condition 的计算结果是 falsy(它可以转换成 false ),那么表达式 exprIfFalse 将会被执行。

使用案例

除了 false,可能的假值表达式还有:nullNaN0 、空字符串( "" )、和 undefined 。如果 condition 是以上中的任何一个, 那么条件表达式的结果就是 exprIfFalse 表达式执行的结果。

一个简单的例子:

var age = 26;
var beverage = (age >= 21) ? "Beer" : "Juice";
console.log(beverage); // "Beer"

一个常见的用法是处理可能为 null 的值:

function greeting(person) {
    var name = person ? person.name : "stranger";
    return "Howdy, " + name;
}
console.log(greeting({name: 'Alice'}));  // "Howdy, Alice"
console.log(greeting(null));             // "Howdy, stranger"

赋值操作符

基础说明

赋值运算符(assignment operator)基于右值(right operand)的值,给左值(left operand)赋值。

简单的赋值运算符,把一个值赋给一个变量。为了把一个值赋给多个变量,可以以链式使用赋值运算符。

Operator: x = y

使用案例

// Assuming the following variables
x = 5
y = 10
z = 25

x = y     // x is 10
x = y = z // x, y and z are all 25

组合说明

每个主要算术操作符,以及个别的其他操作符,都有对应的复合赋值操作符 基本的赋值运算符是等号(=),该运算符把它右边的运算值赋给左边。即,x = yy 的值赋给 x。 其他的赋值运算符通常是标准运算符的简写形式,如下面的定义与示例。

名称 简写形式 含义
赋值(Assignment) x = y x = y
加赋值(Addition assignment) x += y x = x + y
减赋值(Subtraction assignment) x -= y x = x - y
乘赋值(Multiplication assigment x *= y x = x * y
除赋值(Division assignment) x /= y x = x / y
模赋值(Remainder assignment) x %= y x = x % y
指数赋值(Exponentiation assignment) x **= y x = x ** y
左移赋值(Left shift assignment) x <<= y x = x << y
右移赋值(Right shift assignment) x >>= y x = x >> y
无符号右移赋值(Unsigned right shift assignment) x >>>= y x = x >>> y
按位与赋值(Bitwise AND assignment) x &= y x = x & y
按位异或赋值(Bitwise XOR assignment) x ^= y x = x ^ y
按位或赋值(Bitwise OR assignment) x |= y x = x | y

逗号操作符

基础说明

逗号操作符 对它的每个操作数求值(从左到右),并返回最后一个操作数的值。

语法

expr1, expr2, expr3...

参数

expr1, expr2, expr3...
任一表达式。

当你想要在期望一个表达式的位置包含多个表达式时,可以使用逗号操作符。


使用案例

for 循环

假设 a 是一个二维数组,每一维度包含10个元素,则下面的代码使用逗号操作符一次递增/递减两个变量。需要注意的是,var 语句中的逗号_不是_逗号操作符,因为它不是存在于一个表达式中。尽管从实际效果来看,那个逗号同逗号运算符的表现很相似。

但确切地说,它是 var 语句中的一个特殊符号,用于把多个变量声明结合成一个。下面的代码打印一个二维数组中斜线方向的元素

for (var i = 0, j = 9; i <= 9; i++, j--) 
  document.writeln("a[" + i + "][" + j + "] = " + a[i][j]);

处理后返回

另一个使用逗号操作符的例子是在返回值前处理一些操作。 如同下面的代码,只有最后一个表达式被返回,其他的都只是被求值。

function myFunc () {
  var x = 0;
  return (x += 1, x); // the same of return ++x;
}

运算符优先级

详见 MDN Operator_Precedence


今日学习总结


今日心情

今日主要是基于搜索来仔细学习 第 3 章节中的 3.5 操作符,感受到操作符组合的厉害之处~~~,感觉很不错,看了一下 MDN 发现其实还有很多操作符,但是,书上没有写,就暂时没有学习,希望明天学习到更多东西~~~~

最近文章发布一直提示Request Entity Too Large,改了好多版,才发成功,好麻烦~~~

本文使用 mdnice 排版