操作符
一元运算符
只操作一个值的操作符叫一元操作符(unary operator)。
递增/递减操作符
前置操作
前置操作会在表达式最先执行。
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个操作符可以作用于任何值,意思是不限于整数,字符串、布尔值、浮点值,设置对象都可以。递增和递减操作符遵循如下规则:
一元加和减
一元加
一元加由一个加号(+)表示,放在变量前头,对数值没有任何影响
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类型,即只会得到
true
或 false
- 字符串比较,是比较的字符对应的ASCII码(从左往右依次比较,如果第一位一样再比较第二位,以此类推)
- NaN不等于任何值,包括它本身,因此NaN不能参与运算
- 尽量不要比较小数,因为小数有精度问题
- 不同类型之间比较会发生隐式转换,例如 3>'2' //true
- 最终把数据隐式转换成number类型再比较
- 开发中,如果进行准确的比较使用===
= 和 == 和 === 怎么区别?
- = 是赋值。
- == 是判断,只要求值相等,不要求数据类型一样即可返回true。
- === 是全等,要求值和数据类型都一样返回的才是true。
开发中,请使用 ===
比较运算符返回的结果是什么?
相等操作符
如果相等,操作符返回的是布尔类型的 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
是空值(null
或 undefined
)时对其赋值。
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
可选链运算符
?.
可选链运算符
可选链运算符(?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。如果引用是空值(null
或 undefined
),可选链运算符将返回 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中优先级越高越先被执行,优先级相同时以书写顺序从左向右执行
- 一元运算符里面的逻辑非优先级很高
- 逻辑与比逻辑或优先级高
- 乘、除、取余优先级相同,加、减优先级相同,且乘、除、取余优先级大于加、减
- 使用()可以提升优先级