操作符

一元运算符

只操作一个值的操作符叫一元操作符(unary operator)。

  • delete

    delete 运算符用来删除对象的属性。

  • void

    void 运算符表示表达式放弃返回值。

  • typeof

    typeof 运算符用来判断给定对象的类型。

  • +

    一元加运算符将操作转换为 Number 类型。

  • -

    一元减运算符将操作转换为 Number 类型并取反。

  • ~

    按位非运算符。

  • !

    逻辑非运算符。

递增/递减操作符

前置操作

前置操作会在表达式最先执行。

1let n = 1;
2++n;
3console.log(n);	// 2
4
5--n;
6console.log(n);	// 1
7
8let num = 17 + ++n;	// 这里++n会在最先执行
9console.log(num);	// 19

++n 就是 n=n+1的简写形式。

后置操作

后置操作会在表达式最后执行。

1let n = 1;
2let num = 17 + n++; // 这里n++会在最后执行
3console.log(num); // 18
4console.log(n); // 2

注意:前置自增和后置自增单独使用没有区别,如果参与运算就有区别了。

  • 如果++在前,会执行先自增,然后使用自增后的的值进行运算。

  • 如果++在后,会使用原来的值先参与运行,然后自身在进行自增。

这4个操作符可以作用于任何值,意思是不限于整数,字符串、布尔值、浮点值,设置对象都可以。递增和递减操作符遵循如下规则:

  • 对于字符串

    • 如果是有效数值形式,则转换为数值再应用改变。变量类型从字符串变成数值
    • 如果不是有效数值形式,则将变量的值设置为 NaN。变量类型从字符串变成数值
  • 对于布尔值

    • 如果是false,则转换为0再应用改变。变量类型从布尔值变成数值
    • 如果是true,则转换为1再应用改变。变量类型从布尔值变成数值
  • 对于浮点值:加1或减1。

  • 对于对象:调用其 valueOf() 方法取得可以操作的值,对得到的值应用上述规则。如果是 NaN,则调用 toString() 并再次应用其他规则。变量类型从对象编程数值

一元加和减

一元加

一元加由一个加号(+)表示,放在变量前头,对数值没有任何影响

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

如果将一元加应用到非数值,则会执行与使用 Number() 转型函数一样的类型转换。

一元减

一元减由一个减号(-)表示,放在变量前头,主要用于把数值变成负值。

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

在应用到非数值时,一元减会遵循与一元加同样的规则,先对它们进行转换,然后再取负值。

位运算符

ECMAScript中的所有数值都是以 IEEE 754 64位格式存储,但位操作并不直接应用到 64 位表示,而是先把值转换为 32 位整数,再进行位操作,之后再把结果转换为 64 位。

有符号整数使用32位的前31位表示整数值。第32位表示数值的符号,如0表示正,1表示负。

二进制运算符将它们的操作数作为 32 个二进制位(0 或 1)的集合,并返回标准的 JavaScript 数值。

& 按位与

按位与&)运算符在两个操作数对应的二进位都为 1 时,该位的结果值才为 1

1const a = 5; // 00000000000000000000000000000101
2const b = 3; // 00000000000000000000000000000011
3
4console.log(a & b); // 00000000000000000000000000000001
5// Expected output: 1

| 按位或

按位或|)运算符在其中一个或两个操作数对应的二进制位为 1 时,该位的结果值为 1

1const a = 5; // 00000000000000000000000000000101
2const b = 3; // 00000000000000000000000000000011
3
4console.log(a | b); // 00000000000000000000000000000111
5// Expected output: 7

~ 按位非

按位非运算符(~)将操作数的位反转。如同其他位运算符一样,它将操作数转化为 32 位的有符号整型。

1const a = 5; // 00000000000000000000000000000101
2const b = -3; // 11111111111111111111111111111101
3
4console.log(~a); // 11111111111111111111111111111010
5// Expected output: -6
6
7console.log(~b); // 00000000000000000000000000000010
8// Expected output: 2

^ 按位异或

按位异或^)运算符在两个操作数有且仅有一个对应的二进制位为 1 时,该位的结果值为 1

1const a = 5; // 00000000000000000000000000000101
2const b = 3; // 00000000000000000000000000000011
3
4console.log(a ^ b); // 00000000000000000000000000000110
5// Expected output: 6

<< 按位左移运算符

左移操作符 (<<) 将第一个操作数向左移动指定位数,左边超出的位数将会被清除,右边将会补零。

1const a = 5; // 00000000000000000000000000000101
2const b = 2; // 00000000000000000000000000000010
3
4console.log(a << b); // 00000000000000000000000000010100
5// Expected output: 20

>> 按位右移运算符

右移运算符>>)将一个操作数的二进制表示形式向右移动指定位数,该操作数可以是数值或者 BigInt 类型。右边移出位被丢弃,左边移出的空位补符号位(最左边那位)。该操作也称为“符号位传播右移”(sign-propagating right shift)或“算术右移”(arithmetic right shift),因为返回值的符号位与第一个操作数的符号位相同。

1const a = 5; //  00000000000000000000000000000101
2const b = 2; //  00000000000000000000000000000010
3const c = -5; //  11111111111111111111111111111011
4
5console.log(a >> b); //  00000000000000000000000000000001
6// Expected output: 1
7
8console.log(c >> b); //  11111111111111111111111111111110
9// Expected output: -2

>>> 按位无符号右移运算符

无符号右移运算符(>>>(零填充右移)将左操作数计算为无符号数,并将该数字的二进制表示形式移位为右操作数指定的位数,取模 32。向右移动的多余位将被丢弃,零位从左移入。其符号位变为 0,因此结果始终为非负数。与其他按位运算符不同,零填充右移返回一个无符号 32 位整数。

1const a = 5; //  00000000000000000000000000000101
2const b = 2; //  00000000000000000000000000000010
3const c = -5; //  11111111111111111111111111111011
4
5console.log(a >>> b); //  00000000000000000000000000000001
6// Expected output: 1
7
8console.log(c >>> b); //  00111111111111111111111111111110
9// Expected output: 1073741822

布尔操作符

二元逻辑运算符典型的用法是用于布尔(逻辑)值运算,它们返回布尔值。

&& 逻辑与

当且仅当所有操作数为 true 时,一组布尔操作数的逻辑与&&,逻辑连接)运算结果为 true,否则为 false

一般来说,当从左到右求值时,该操作符返回第一个假值操作数的值;如果它们都是真值,则返回最后一个操作数的值。

1const a = 3;
2const b = -2;
3
4console.log(a > 0 && b > 0); // false

|| 逻辑或

对于一组操作数的逻辑或||,逻辑析取)运算符,当且仅当其一个或多个操作数为真,其运算结果为真。它通常与布尔(逻辑)值一起使用。当它是布尔值时,返回一个布尔值。然而,|| 运算符实际上是返回一个指定的操作数的值,所以如果这个运算符被用于非布尔值,它将返回一个非布尔值。

1const a = 3;
2const b = -2;
3
4console.log(a > 0 || b > 0); // true

! 逻辑非

逻辑非!,逻辑连接取反)运算符将真值或假值转换为对应的相反值,经常用于布尔(逻辑)值。当与非布尔值使用时,如果其操作数可以转化为 true,则返回 false,否则返回 true

1const a = 3;
2const b = -2;
3
4console.log(!(a > 0 || b > 0)); // false

?? 空值合并运算符

空值合并运算符,如果 ?? 前面是 null 或 undefined,取后面的默认值。

1const foo = null ?? 'default string';
2console.log(foo); // default string
3
4const baz = 0 ?? 42;
5console.log(baz); // 0

算术运算符

算术运算符以二个数值(字面量或变量)作为操作数,并返回单个数值。

+ 加法操作符

加法+)运算符计算数字操作数或字符串连接的总和。

1console.log(2 + 2);
2// Expected output: 4
3
4console.log(2 + true);
5// Expected output: 3
6
7console.log('hello ' + 'everyone');
8// Expected output: "hello everyone"
9
10console.log(2001 + ': A Space Odyssey');
11// Expected output: "2001: A Space Odyssey"

- 减法操作符

减法-)运算符将两个操作数相减,并产生两者之差。

1console.log(5 - 3);
2// Expected output: 2
3
4console.log(3.5 - 5);
5// Expected output: -1.5
6
7console.log(5 - 'hello');
8// Expected output: NaN
9
10console.log(5 - true);
11// Expected output: 4

/ 除法操作符

除法/)运算符计算两个操作数的商,其中左操作数是被除数,右操作数是除数。

1console.log(12 / 2);
2// Expected output: 6
3
4console.log(3 / 2);
5// Expected output: 1.5
6
7console.log(6 / '3');
8// Expected output: 2
9
10console.log(2 / 0);
11// Expected output: Infinity

* 乘法操作符

乘法*)运算符计算操作数的乘积。

1console.log(3 * 4);
2// Expected output: 12
3
4console.log(-3 * 4);
5// Expected output: -12
6
7console.log('3' * 2);
8// Expected output: 6
9
10console.log('foo' * 2);
11// Expected output: NaN

% 取模操作符

取余%)运算符返回左侧操作数除以右侧操作数的余数。它总是与被除数的符号保持一致。

1console.log(13 % 5);
2// Expected output: 3
3
4console.log(-13 % 5);
5// Expected output: -3
6
7console.log(4 % 2);
8// Expected output: 0
9
10console.log(-4 % 2);
11// Expected output: -0

** 指数操作符

**)运算符返回第一个操作数取第二个操作数的幂的结果。它等价于 Math.pow(),不同之处在于,它还接受 BigInt 作为操作数。

1console.log(3 ** 4);
2// Expected output: 81
3
4console.log(10 ** -2);
5// Expected output: 0.01
6
7console.log(2 ** (3 ** 2));
8// Expected output: 512
9
10console.log((2 ** 3) ** 2);
11// Expected output: 64

关系操作符

比较运算符比较两个操作数并返回基于比较结果的布尔值。

in

in 运算符用来判断对象是否拥有给定属性。

如果指定的属性在指定的对象或其原型链中,则 in 运算符返回 true

1const car = { make: 'Honda', model: 'Accord', year: 1998 };
2
3console.log('make' in car);
4// Expected output: true
5
6delete car.make;
7if ('make' in car === false) {
8  car.make = 'Suzuki';
9}
10
11console.log(car.make);
12// Expected output: "Suzuki"

instanceof

instanceof 运算符判断一个对象是否是另一个对象的实例。

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

1function Car(make, model, year) {
2  this.make = make;
3  this.model = model;
4  this.year = year;
5}
6const auto = new Car('Honda', 'Accord', 1998);
7
8console.log(auto instanceof Car);
9// Expected output: true
10
11console.log(auto instanceof Object);
12// Expected output: true

< 小于

小于<)运算符在左操作数比右操作数小时返回 true,否则返回 false

1console.log(5 < 3);
2// Expected output: false
3
4console.log(3 < 3);
5// Expected output: false
6
7// Compare bigint to number
8console.log(3n < 5);
9// Expected output: true
10
11console.log('aa' < 'ab');
12// Expected output: true

> 大于

大于运算符(>)在左操作数大于右操作数时返回 true,否则返回 false

1console.log(5 > 3);
2// Expected output: true
3
4console.log(3 > 3);
5// Expected output: false
6
7// Compare bigint to number
8console.log(3n > 5);
9// Expected output: false
10
11console.log('ab' > 'aa');
12// Expected output: true

<= 小于等于

小于等于运算符(<=)在左操作数小于等于右操作数时返回 true,否则返回 false

1console.log(5 <= 3);
2// Expected output: false
3
4console.log(3 <= 3);
5// Expected output: true
6
7// Compare bigint to number
8console.log(3n <= 5);
9// Expected output: true
10
11console.log('aa' <= 'ab');
12// Expected output: true

>= 大于等于

大于等于运算符(>=)在左操作数大于等于右操作数时返回 true,否则返回 false

1console.log(5 >= 3);
2// Expected output: true
3
4console.log(3 >= 3);
5// Expected output: true
6
7// Compare bigint to number
8console.log(3n >= 5);
9// Expected output: false
10
11console.log('ab' >= 'aa');
12// Expected output: true

下面来体验不同类型的比较结果:

1let a = 1,b = 2,c = '1';
2
3console.log(a < b); //true
4console.log(a == b); //false
5console.log(a == c); //true
6console.log(a === c); //false
7console.log(a == true); //true
8console.log(a === true); //false

注意:

  • 比较结果为boolean类型,即只会得到 truefalse
  • 字符串比较,是比较的字符对应的ASCII码(从左往右依次比较,如果第一位一样再比较第二位,以此类推)
  • NaN不等于任何值,包括它本身,因此NaN不能参与运算
  • 尽量不要比较小数,因为小数有精度问题
  • 不同类型之间比较会发生隐式转换,例如 3>'2' //true
    • 最终把数据隐式转换成number类型再比较
    • 开发中,如果进行准确的比较使用===

= 和 == 和 === 怎么区别?

  • = 是赋值。
  • == 是判断,只要求值相等,不要求数据类型一样即可返回true。
  • === 是全等,要求值和数据类型都一样返回的才是true。

开发中,请使用 ===

比较运算符返回的结果是什么?

  • 结果只有2个, true 或者 false

相等操作符

如果相等,操作符返回的是布尔类型的 true,否则是 false。

== 相等

相等运算符(==)检查其两个操作数是否相等,返回一个布尔值结果。与严格相等运算符(===)不同,它会比较不同类型的操作数,并尝试强制类型转换。

1console.log(1 == 1);
2// Expected output: true
3
4console.log('hello' == 'hello');
5// Expected output: true
6
7console.log('1' == 1);
8// Expected output: true
9
10console.log(0 == false);
11// Expected output: true

!= 不等

不相等运算符(!=)检查其两个操作数是否不相等,并返回布尔结果。与严格不相等运算符不同,它会转换并比较不同类型的操作数。

1console.log(1 != 1);
2// Expected output: false
3
4console.log('hello' != 'hello');
5// Expected output: false
6
7console.log('1' != 1);
8// Expected output: false
9
10console.log(0 != false);
11// Expected output: false

=== 全等

严格相等运算符(===)会检查它的两个操作数是否相等,并且返回一个布尔值结果。与相等运算符不同,严格相等运算符总是认为不同类型的操作数是不同的。

1console.log(1 === 1);
2// Expected output: true
3
4console.log('hello' === 'hello');
5// Expected output: true
6
7console.log('1' === 1);
8// Expected output: false
9
10console.log(0 === false);
11// Expected output: false

!== 非全等

严格不相等运算符(!==)检查它的两个对象是否不相等,返回一个布尔结果。与不相等运算符不同,严格不相等运算符总是认为不同类型的对象是不同的。

1console.log(1 !== 1);
2// Expected output: false
3
4console.log('hello' !== 'hello');
5// Expected output: false
6
7console.log('1' !== 1);
8// Expected output: true
9
10console.log(0 !== false);
11// Expected output: true

赋值操作符

=

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

1let num = 10; // 将 10 赋值给左边的变量 num

+=

加法赋值运算符(+=)将右操作数的值添加到变量,并将结果分配给该变量。两个操作数的类型决定了加法赋值运算符的行为,可能为加法或拼接。

1let num = 10;
2num += 5; // num = num + 5
3console.log(num); // 15

-=

减法赋值-=)运算符从变量中减去右操作数的值,并将结果赋值给该变量。

1let num = 10;
2num -= 5; // num = num - 5
3console.log(num); // 5

*=

乘法赋值*=)运算符将变量乘以右操作数的值,并将结果赋值给该变量。

1let num = 10;
2num *= 5; // num = num * 5
3console.log(num); // 50

/=

除法赋值/=)运算符将变量除以右操作数的值,并将结果赋值给该变量。

1let num = 10;
2num /= 5; // num = num / 5
3console.log(num); // 2

%=

取余赋值%=)运算符将变量除以右操作数的值,并将余数赋值给该变量。

1let num = 10;
2num %= 5; // num = num % 5
3console.log(num); // 0

**=

幂赋值**=)对两个操作数执行幂运算,并将结果赋给左操作数。

1let num = 10;
2num **= 5; // num = num ** 5
3console.log(num); // 100000

<<=

左移赋值运算符(<<=)将变量向左移动指定的位数,并将结果赋值给变量。

1let num = 5; // 00000000000000000000000000000101
2
3num <<= 2; // 00000000000000000000000000010100
4
5console.log(num); // 20

>>=

右移赋值运算符(>>=)将变量向右移动指定的位数,并将结果赋值给变量。

1let a = 5; //  00000000000000000000000000000101
2
3a >>= 2; //  00000000000000000000000000000001
4console.log(a);
5// Expected output: 1
6
7let b = -5; //  11111111111111111111111111111011
8
9b >>= 2; //  11111111111111111111111111111110
10console.log(b);
11// Expected output: -2

>>>=

无符号右移赋值(>>>=)运算符向右移动移动指定(二进制)位数,并将结果赋值给变量。

1let a = 5; //  00000000000000000000000000000101
2
3a >>>= 2; //  00000000000000000000000000000001
4console.log(a);
5// Expected output: 1
6
7let b = -5; // -00000000000000000000000000000101
8
9b >>>= 2; //  00111111111111111111111111111110
10console.log(b);
11// Expected output: 1073741822

&=

按位与赋值运算符(&=)使用两个操作数的二进制表示,对它们进行按位与运算并将结果赋值给变量。

1let num = 5; // 00000000000000000000000000000101
2num &= 3; // 00000000000000000000000000000011
3
4console.log(num); // 1

^=

按位异或赋值操作符 (^=) 使用二进制表示操作数,进行一次按位异或操作并赋值。

1let a = 5; // 00000000000000000000000000000101
2a ^= 3; // 00000000000000000000000000000011
3
4console.log(a); // 00000000000000000000000000000110
5// Expected output: 6

|=

按位或赋值(|=)运算符使用两个操作数的二进制表示,对它们执行按位或运算并将结果分配给变量。

1let a = 5; // 00000000000000000000000000000101
2a |= 3; // 00000000000000000000000000000011
3
4console.log(a); // 00000000000000000000000000000111
5// Expected output: 7

&&=

逻辑与赋值x &&= y)运算仅在 x 为真值时为其赋值。

1let a = 1;
2let b = 0;
3
4a &&= 2;
5console.log(a);
6// Expected output: 2
7
8b &&= 2;
9console.log(b);
10// Expected output: 0

||=

逻辑或赋值(x ||= y)运算仅在 x 为假值时为其赋值。

1const a = { duration: 50, title: '' };
2
3a.duration ||= 10;
4console.log(a.duration);
5// Expected output: 50
6
7a.title ||= 'title is empty.';
8console.log(a.title);
9// Expected output: "title is empty"

??=

逻辑空赋值运算符(x ??= y)仅在 x 是空值(nullundefined)时对其赋值。

1const a = { duration: 50 };
2
3a.duration ??= 10;
4console.log(a.duration);
5// Expected output: 50
6
7a.speed ??= 25;
8console.log(a.speed);
9// Expected output: 25

可选链运算符

?. 可选链运算符

可选链运算符?.)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。如果引用是空值(nullundefined),可选链运算符将返回 undefined 而不是导致错误。

1const IronManInfo = {
2  name: "Tony",
3};
4
5console.log(IronManInfo?.age); // undefined
6
7// 与函数调用一起使用时,如果给定的函数不存在,则返回 undefined
8console.log(IronManInfo.getArmorModel?.()); // undefined

条件(三元)运算符

?: 条件运算符

条件(三元)运算符是 JavaScript 唯一使用三个操作数的运算符:一个条件后跟一个问号(?),如果条件为真值,则执行冒号(:)前的表达式;若条件为假值,则执行最后的表达式。该运算符经常当作 if...else 语句的简捷形式来使用。

1function getFee(isMember) {
2  return isMember ? '$2.00' : '$10.00';
3}
4
5console.log(getFee(true));
6// Expected output: "$2.00"
7
8console.log(getFee(false));
9// Expected output: "$10.00"

逗号运算符

, 逗号运算符

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

1let x = 1;
2x = (x++, x);
3console.log(x); // 2
4
5x = (2, 3);
6console.log(x); // 3

优先级

同时使用多个运算符编写程序时,会按着某种顺序先后执行,我们称为优先级

JavaScript中优先级越高越先被执行,优先级相同时从左向右执行。

优先级 运算符 顺序
1 小括号 ( )
2 一元运算符 ++ -- !
3 算术运算符 先 * / % 后 + -
4 关系运算符 > >= < <=
5 相等运算符 == != === !==
6 逻辑运算符 先 && 后 ||
7 赋值运算符 =
8 逗号运算符 ,
  • JavaScript中优先级越高越先被执行,优先级相同时以书写顺序从左向右执行
    • 一元运算符里面的逻辑非优先级很高
    • 逻辑与比逻辑或优先级高
    • 乘、除、取余优先级相同,加、减优先级相同,且乘、除、取余优先级大于加、减
    • 使用()可以提升优先级