本文将逐一讨论JavaScript中的运算符。并演示它们的功能和用法,帮助大家能够理解运算符在构建复杂表达式中的作用。
JavaScript运算符是什么?
花点时间查看以下代码片段:
jsx
const x = 10, y = 20, z = 30;
console.log((x > y ? x : y) > z ? (x > y ? x : y) : z); // 令人疑惑吗?
如果你不能很快知道答案,请仔细阅读本文,再回来看这道题。
术语简介
在我们深入研究之前,介绍一些运算符常用的术语:
- **操作数:**是运算符操作的项目。如果我们将运算符视为一种操作,那么操作数就是该操作应用于的对象。例如,在表达式5 + 3中,
+
是运算符(加法操作),而5和3是操作数,即要相加的数字。在JavaScript中,操作数可以是不同类型的,如数字、字符串、变量,甚至更复杂的表达式。 - **强制类型转换:**是将一个值从一种原始类型转换为另一种原始类型的过程。例如,JavaScript可能会将数字更改为字符串,或者将非布尔值更改为布尔值。JavaScript中的原始类型包括
String
、Number
、BigInt
、Boolean
、undefined
、Symbol
或null
。 NaN
:代表不是数字 。它是Number
类型的特殊值,表示无效或无法表示的数值。- 真值:是在布尔上下文中评估为
true
的值,而假值 在布尔上下文中评估为false
,假值包括false
、0
、0
、''
、null
、undefined
、NaN
和BigInt(0)
。
算术运算符
算术运算符 允许我们对值执行算术运算,并对数据进行转换。JavaScript中常用的算术运算符包括加法(+
)、减法(-
)、乘法(*
)和除法(/
)。除此之外,还有取模运算符(%
),它返回除法操作的余数,以及增量(++
)和减量(--
)运算符,它们可以将值增加或减少1。
加法:+
加法运算符 执行两种不同的操作:数字相加和字符串连接。在使用+
运算符计算表达式时,JavaScript首先将两个操作数转换为原始值。完成此操作后,它会检查两个操作数的类型。
如果一个操作数是字符串,则将另一个操作数也转换为字符串,然后连接这两个字符串。例如:
jsx
'Hello, ' + 'World!' // Hello, World!
'The number is ' + 42 // 'The number is 42'
如果两个操作数都是BigInts
,则执行BigInt
加法。BigInt
是一种特殊的数值类型,可以处理标准Number
类型无法处理的大数值。
但如果一个操作数是BigInts
,另一个不是,JavaScript会抛出TypeError
:
jsx
const num1 = BigInt(12345678901234567890);
const num2 = BigInt(12345678901234567890);
num1 + num2 // 24691357802469134336n
num1 + 1 // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
对于所有其他情况,两个操作数都将转换为数字,并执行数字相加。例如:
jsx
10 + 20 // 30
10 + true // 11 --- true会转换为值1
请注意,JavaScript有一些奇怪的输出:
jsx
1 + { a: 1 } // '1[object Object]'
在这种情况下,JavaScript试图将对象{ a: 1 }
转换为原始值,但最好它能做的就是将其转换为字符串[object Object]
,然后与数字1连接起来。
减法:``
JavaScript中的减法运算符 在使用上很简单:用一个数字减去另一个数字。与加法运算符一样,它也适用于BigInt
类型。
如果两个操作数是数字或可以转换为数字,JavaScript执行数字减法:
jsx
10 - 3 // 7
'100' - 30 // 70
如果两个操作数都是BigInts
,JavaScript执行BigInt
减法:
jsx
const num1 = BigInt('9007199254740993');
const num2 = BigInt('3');
num1 - num2 // 900719925474
0990n
与加法运算符一样,当与非数字一起使用时,减法也可能产生意想不到的结果。例如,如果我们尝试减去无法转换为数字的内容,JavaScript将返回NaN
,表示"不是数字":
jsx
10 - 'Jim' // NaN
乘法:
乘法运算符适用于数字和BigInts
。
通常我们将对数字进行乘法运算:
jsx
10 * 5 // 50
如果两个操作数都是BigInts
,则执行BigInt
乘法:
jsx
const num1 = 9007199254740993n;
num1 * 2n // 18014398509481986n
与其他运算符一样,JavaScript尝试将非数字值转换为数字。如果无法这样做,它将返回NaN
:
jsx
'100' * 30 // 3000
10 * 'Jim' // NaN
除法:/
除法运算符 (/
)与数字和BigInts
一起使用,方式与+
、-
和*
类似。它首先将两个操作数转换为数字。
标准数值除法:
jsx
10 / 2 // 5
10 / '2' // 5
处理BigInts
时,除法运算符的行为稍有不同。它执行除法并丢弃任何余数,从效果上来说是向零截断结果:
jsx
const num = 10n;
num / 3n // 3n,而不是3.333...n
除以零的结果将产生Infinity
,除非是BigInt
,在这种情况下会引发RangeError
。
如果我们尝试将数字除以无法转换为数字的值,JavaScript通常会返回NaN
。
模数(余数):%
模数运算符 用于找到一个数除以另一个数(称为被除数 和除数 )后的余数。这个算术运算符适用于数字和BigInts
。
当我们使用%
运算符时,JavaScript首先将操作数转换为数字:
jsx
10 % 3 // 1
'10' % 3 // 1
这是因为三除以十等于三(得到九),余下来的(余数)是一。
这个运算符的一个常见用例是检查一个数是奇数还是偶数:
jsx
const isEven = num => num % 2 === 0;
isEven(1) // false
isEven(2) // true
这里使用了一个箭头函数和稍后会介绍的三重等号运算符。
模数运算符有一些特殊情况。例如,如果操作数中的一个是NaN
,如果被除数是Infinity
,或者如果除数是0
,则操作返回NaN
。
另一方面,如果除数是Infinity
,或者被除数是0
,则操作返回被除数。
增量:++
增量运算符用于将变量的值增加1。它可以应用于Number
或BigInt
类型的操作数,根据它是后缀形式还是前缀形式,其行为会有所不同。
后缀增量
如果运算符放在操作数后面(num++
),则增量操作在返回值之后执行。在这种情况下,变量的原始值在当前表达式中使用,然后在之后递增变量的值:
jsx
let num = 5;
const result = num++;
console.log(result); // 5
console.log(num); // 6
前缀增量
如果运算符放在操作数前面(++num
),则增量操作在返回值之前执行。在这种情况下,首先递增变量的值,然后在当前表达式中使用更新后的值:
jsx
let num = 5;
const result = ++num;
console.log(result); // 6
console.log(num); // 6
减量:``
减量运算符 用于将变量的值减少1。与增量运算符类似,它可以应用于Number
或BigInt
类型的操作数,根据它是后缀形式还是前缀形式,其行为会有所不同。
后缀减量
当运算符放在操作数后面(num--
)时,减量操作在返回值之后执行。在这种情况下,变量的原始值在当前表达式中使用,然后在之后递减变量的值:
jsx
let num = 5;
const result = num--;
console.log(result); // 5
console.log(num); // 4
前缀减量
当运算符放在操作数前面(--num
)时,减量操作在返回值之前执行。在这种情况下,首先递减变量的值,然后在当前表达式中使用更新后的值:
jsx
let num = 5;
const result = --num;
console.log(result); // 4
console.log(num); // 4
与增量运算符一样,减量运算符只能用于可以更改的变量:
jsx
const num = 5;
const result = num--; // Uncaught TypeError: Assignment to constant variable.
其他算术运算符
除了增量和减量运算符,JavaScript中还有其他一些算术运算符。
一元否定运算符 (-
)用于否定数值,将其符号更改为相反的。例如,-5
将是数字5
的否定。
一元加运算符 (+
)可用于显式将值转换为数字,当处理数字的字符串表示时很有用。例如,+'10'
将字符串'10'
转换为数字10
:
jsx
const num = '10';
const res = +num; // 一元运算符将String转换为Number
res // 10
指数运算符 (**
)用于将一个数值提升到某个幂次方。例如,2 ** 3
表示2的3次幂,结果是8。
还需要注意的是,JavaScript遵循运算符优先级规则,这些规则确定表达式中运算符的评估顺序。例如,乘法和除法的优先级高于加法和减法,因此它们首先进行执行:
jsx
const result = 10 + 5 * 2; // 先进行乘法运算
console.log(result); // 20
我们可以使用分组运算符()
来更改执行的顺序,这在下面的分组运算符部分中进行了介绍。
赋值运算符
赋值运算符 用于将值赋给变量。它们还提供了一种简洁有效的方式,根据表达式或其他值来更新变量的值。除了基本的赋值运算符(=
)之外,JavaScript 还提供了复合赋值运算符,它们将算术或逻辑操作与赋值结合在一起。
赋值:=
此运算符用于将一个值赋给一个变量。它允许我们将一个值存储在变量中,以便我们可以在代码中稍后使用和引用它:
jsx
const num = 4; // 将值 4 赋给变量 num
const squared = num * num; // 将值 16 赋给变量 squared
赋值运算符将运算符右侧的值分配给左侧的变量。
此外,=
运算符可以链接在一起,以在一行中将相同的值分配给多个变量:
jsx
const a = b = c = 10; // 将值 10 分配给变量 a、b 和 c
console.log(a, b, c); // 输出 10 10 10
加法赋值:+=
加法赋值运算符 是一种复合运算符,它执行一步操作和赋值。具体来说,它将右操作数加到左操作数上,然后将结果分配给左操作数:
jsx
let num = 10;
num += 5 // 15
这个运算符不仅限于数字。它也可以用于字符串连接:
jsx
let greeting = 'Hello, ';
greeting += 'World!' // 'Hello, World!'
当操作数不是相同类型时,JavaScript 会应用与类型强制转换相同的规则,就像我们之前看到的一样:
jsx
let greeting = 'Hello, ';
greeting += 42 // 'Hello, 42'
减法赋值:=
减法赋值运算符 是另一种复合运算符,它执行一步操作和赋值。它从左操作数中减去右操作数,然后将结果分配给左操作数:
jsx
let num = 10;
num -= 5 // 5
与其他 JavaScript 运算符一样,当操作数不是相同类型时,-=
运算符会在进行比较时进行类型强制转换。如果可以将一个操作数转换为数字,JavaScript 就会这样做:
jsx
let num = '10';
num -= 5 // num 现在是 5(数字),而不是 '5'(字符串)
否则,结果是 NaN
:
jsx
let name = 'Jim';
name -= 5 // NaN
乘法赋值:=
乘法赋值运算符 将左操作数乘以右操作数,然后将结果分配回左操作数:
jsx
let num = 5;
num *= 3 // 15
当我们使用不同类型的操作数时,JavaScript 会尝试将非数字字符串操作数转换为数字:
jsx
let num = '5';
num *= 3 // num 现在是 15(数字),而不是 '15'(字符串)
如果字符串操作数无法转换为数字,则结果为 NaN
。
除法赋值:/=
与其兄弟运算符一样,除法赋值运算符 在两个操作数上执行操作,然后将结果分配回左操作数:
jsx
let num = 10;
num /= 2 // 5
否则,我们在上面关于除法运算符的部分讨论过的规则也适用:
jsx
let num = 3;
num /= '-0.5' // -6
num = 3;
num /= 0 // Infinity
num = 3;
num /= 'Jim' // NaN
取模赋值:%=
取模赋值运算符 对两个操作数执行取模运算,然后将结果分配给左操作数:
jsx
let num = 10;
num %= 3 // 1
否则,我们在上面关于取模运算符的部分讨论过的规则也适用:
jsx
let num = 3;
num %= '3' // 0
num = 3;
num %= 0 // NaN
num = 3;
num %= 'Jim' // NaN
指数赋值:*=
指数赋值运算符执行指数运算,其中左操作数是底数,右操作数是指数,然后将结果分配给左操作数:
jsx
let num = 2;
num **= 3 // 8
与之前一样,当第二个操作数不是数字时,JavaScript 将尝试以不同程度的成功进行转换:
jsx
let num = 2;
num **= '3' // 8
num = 2;
num **= 'Jim' // NaN
位运算赋值运算符
我们上面一直关注算术运算符,但 JavaScript 还支持在位级别上运作的赋值运算符。这些是位运算赋值运算符。这些运算符包括:
- 位与赋值(
&=
) - 位或赋值(
|=
) - 位异或赋值(
^=
) - 左移位赋值(
<<=
) - 右移位赋值(
>>=
) - 无符号右移位赋值(
>>>=
)
这些 JavaScript 赋值运算符中的每一个都执行与操作数的二进制表示的特定位运算,并将结果分配回左操作数。
比较运算符
JavaScript 运算符的另一重要运算符:比较运算符==
、===
。顾名思义,比较运算符 用于比较值并返回布尔结果。比较运算符是许多编程决策和控制结构的基础,从简单的条件检查到复杂的逻辑操作都必不可少。
相等性:==
相等运算符 用于检查两个值是否相等。它返回一个布尔结果。要注意是,此比较运算符执行松散的相等性检查,这意味着如果操作数的类型不同,JavaScript 将尝试将它们转换为公共类型,然后再进行比较:
jsx
1 == 1 // true,两个操作数都是数字
1 == '1' // true,字符串被转换为数字
当涉及到对象和数组时,==
运算符检查它们是否引用内存中的相同位置,而不是它们的内容是否相同:
jsx
const array1 = [1, 2, 3];
const array2 = [1, 2, 3];
array1 == array2 // false,尽管它们看起来一样
const array3 = array1;
array1 == array3; // true,它们引用相同的内存位置
在这种情况下,JavaScript 不会尝试转换和比较对象或数组中的值。相反,它检查它们是否是相同的对象(即它们是否占用相同的内存空间)。
不等性:!=
不等运算符 用于检查两个值是否不相等。与 ==
运算符类似,它执行松散的不等比较。这意味着,如果它们是不同类型,它会尝试将操作数转换为公共类型,然后再进行比较:
jsx
1 != 2 // true,1 不等于 2
1 != '1' // false,字符串被转换为数字,1 等于 1
'apple' != 'orange' // true,字符串不同
与 ==
运算符类似,当涉及对象和数组时,!=
运算符检查它们是否引用内存中的相同位置,而不是它们的内容是否相同。
严格相等性 (===
) 和严格不等性 (!==
)
严格相等性 和 严格不等性 比较运算符类似于它们的非严格对应物 (==
和 !=
),但它们不执行类型强制转换。如果操作数是不同类型的,无论它们的值是否相等,它们都被视为不同。
以下是它们的工作原理:
jsx
1 === '1' // false,因为 Number 不严格等于 String
1 !== '1' // true,因为 Number 不严格等于 String
null === undefined // false,即使 null == undefined 为 true
true === 1 // false,因为 Boolean 不严格等于 Number
对于对象和数组,严格相等运算符的行为与松散相等运算符相同:它检查它们是否引用相同的对象,而不是它们的内容是否相同。
NaN
是一个特殊情况。它是 JavaScript 中唯一不严格等于自身的值:
jsx
NaN === NaN // false
大于:>
大于运算符 检查左操作数是否大于右操作数,返回一个布尔结果。这种比较对数值值来说很简单:
jsx
4 > 3 // true
2 > 2 // false
当比较非数值值时,JavaScript 应用一种转换过程,使它们可以进行比较。如果两个值都是字符串,它们将根据它们在 Unicode 字符集中的相应位置进行比较:
jsx
'3' > '2' // true
'abc' > 'def' // false
如果一个或两个操作数不是字符串,JavaScript 将尝试将它们转换为比较用的数字:
jsx
'10' > 2 // true,将 '10' 转换为数字进行比较
在这种转换过程中,特定值的特殊规则适用。例如,null
被转换为 0
,undefined
被转换为 NaN
,而布尔值 true
和 false
分别被转换为 1
和 0
。但是,如果在转换后任何一个操作数是 NaN
,则运算符始终返回 false
:
jsx
10 > 'Jim' // false,将 'Jim' 转换为数字得到 NaN
小于:<
小于运算符 如果左操作数小于右操作数,则返回 true
,否则返回 false
。与大于运算符一样,只是操作数的顺序相反:
jsx
5 < 10 // true,5 小于 10
'10' <
'2' // true,因为比较是按字典顺序进行的
'10' < 2 // false,字符串 '10' 被强制转换为数字
与 >
运算符一样,<
运算符在进行比较之前使用强制类型转换将操作数转换为公共类型。
大于等于 (>=
) 和小于等于 (<=
)
大于等于 (>=
) 和 小于等于 (<=
) 运算符与它们的 <
和 >
对应物类似,只是多了一个相等条件。
对于 >=
运算符,如果左操作数大于或等于右操作数,则返回 true
,否则返回 false
。相反,对于 <=
运算符,如果左操作数小于或等于右操作数,则返回 true
,否则返回 false
。强制类型转换和类型转换规则与上面介绍的 <
和 >
运算符相同:
jsx
5 >= 5 // true,5 等于 5
5 >= 6 // false,5 不大于 6
'10' >= '2' // false,'1' 的 Unicode 为 49,而 '2' 的 Unicode 为 50
'10' <= 20 // true,字符串 '10' 被强制转换为数字
5 >= false // true,false 被强制转换为 0
5 <= true // false,true 被强制转换为 1
null >= -1 // true,null 被强制转换为 0
undefined <= 0 // false,undefined 被强制转换为 NaN
逻辑运算符
JavaScript 中的 逻辑运算符 提供了一种同时处理多个条件的方式。它们是编程中决策构造的重要组成部分,例如 if
语句和 for
循环。
逻辑与:&&
当与布尔值一起使用时,逻辑与运算符返回 true
,如果所有条件都为 true
,否则返回 false
。
然而,与非布尔值一起使用时,它变得非常有趣:
- 该运算符从左到右评估条件。
- 如果它遇到一个可以转换为
false
(称为假值)的值,它会停止并返回该值。 - 如果所有值都为真值,则返回最后一个真值。
例如:
jsx
true && true // true
true && false // false
'apple' && 'banana' // 'banana'
'' && 'banana' // ''
&&
运算符返回操作数的值,使其成为条件执行和设置默认值的多功能工具。例如,我们可以使用 &&
运算符仅在满足某个条件时执行函数或一段代码:
jsx
const userIsLoggedIn = true;
userIsLoggedIn && renderWelcomeMessage();
在这种情况下,只有当 userIsLoggedIn
为 true
时,才会执行 renderWelcomeMessage
。如果 userIsLoggedIn
为 false
,操作将在 userIsLoggedIn
处停止,renderWelcomeMessage
将不会被调用。这种模式在 React 中经常用于有条件地渲染组件。
逻辑或:||
当与布尔值一起使用时,逻辑或运算符返回 true
,如果至少一个条件为 true
,否则返回 false
。
它还可以返回非布尔值,执行所谓的短路求值。它从左到右评估条件,停在并返回第一个真值条件的值。如果所有条件都是假值,它将返回最后一个假值:
jsx
true || false // true
false || true // true
'Hello' || 'World' // 'Hello'
'' || 'World' // 'World'
0 || '' // ''
逻辑非:!
逻辑非运算符用于反转条件或表达式的布尔值。与 &&
和 ||
运算符不同,!
运算符始终返回布尔值。
如果条件为真值(即可以转换为 true
),则该运算符返回 false
。如果条件为假值(即可以转换为 false
),则该运算符返回 true
:
jsx
!true // false
!false // true
!'Hello' // false
!'' // true
!0 // true
我们可以使用 !
运算符两次将一个值转换为其布尔等效值:
jsx
!!'Hello' // true
!!'' // false
!!0 // false
空值合并运算符:??
空值合并运算符 用于检查其左侧的值是否为 null
或 undefined
,如果是,它将返回右侧的值。否则,它将返回左侧的值。
尽管在某些方面与 ||
运算符相似,但 ??
运算符在处理假值时有所不同。
而 ||
运算符在左侧操作数为任何假值(如 null
、undefined
、false
、0
、NaN
、''
)时会返回右操作数,??
运算符只在左操作数为 null
或 undefined
时才会返回右操作数:
jsx
null ?? '默认字符串' // '默认字符串'
undefined ?? '默认字符串' // '默认字符串'
'' ?? '默认字符串' // ''
0 ?? 100 // 0
使用逻辑运算符设置默认值
||
和 ??
逻辑运算符在程序中设置默认值时非常有用。下面是使用 ||
运算符进行设置默认值的示例:
jsx
const userColorPreference = null;
const defaultColor = '蓝色';
const color = userColorPreference || defaultColor; // '蓝色'
这是使用 ??
运算符的示例:
jsx
const userColorPreference = null;
const defaultColor = '蓝色';
const color = userColorPreference ?? defaultColor; // '蓝色'
这些逻辑运算符之间的主要区别(如上所示)在于它们如何处理假值:
jsx
const userColorPreference = '';
const defaultColor = '蓝色';
const color1 = userColorPreference || defaultColor; // '蓝色'
const color2 = userColorPreference ?? defaultColor; // ''
位运算符
JavaScript 中的位运算符提供了一种在二进制级别执行操作的方式,直接操作数字的二进制表示。虽然这些运算符在特定任务(如数据编码、解码和处理)中可能很有用,但在日常 JavaScript 编程中并不经常使用。
位与运算符:&
位与运算符 对整数的二进制表示进行位与操作。它返回一个新数字,其中的位如果在两个操作数中的相同位置上都为 1,则设置为 1。否则,将其设置为 0:
jsx
// 5 的二进制表示:101
// 3 的二进制表示:011
5 & 3 // 1(对应二进制 001)
位或运算符:|
位或运算符 的工作方式类似于 &
运算符,但它设置为 1 的位,如果操作数中相同位置上的位至少有一个为 1:
jsx
// 5 的二进制表示:101
// 3 的二进制表示:011
5 | 3 // 7(对应二进制 111)
位异或运算符:^
位异或运算符 有点不同。它仅在操作数中的相同位置上的位不同(一个为 1,另一个为 0)时设置位为 1:
jsx
// 5 的二进制表示:101
// 3 的二进制表示:011
5 ^ 3 // 6(对应二进制 110)
位非运算符:~
位非运算符 (~
) 反转其操作数的位。它将二进制表示中的 1 变为 0,0 变为 1:
jsx
// 5 的二进制表示:101
~5 // -6(对应二进制的补码,即 010)
注意:二进制补码 是一种表示负整数的二进制符号表示方法。
位移运算符:<<, >>, >>>
位移运算符 用于将二进制数的位向左或向右移动。在 JavaScript 中,有三种类型:左位移 (<<
)、右位移 (>>
) 和零填充右位移 (>>>
)。
左位移位运算符 (<<
) 将位向左移动,并在右侧填充零。右位移运算符 (>>
) 将位向右移动,舍弃移出的位。零填充右位移运算符 (>>>
) 也将位向右移动,但在左侧填充零。
这些运算符在日常 JavaScript 编程中较少使用,但在更专业的领域,如低级编程、二进制数据处理和某些类型的数学计算中有用。
其他运算符
除了常用的算术、比较、逻辑和位运算符外,JavaScript 还提供了一些特定用途的独特运算符。这些包括用于处理条件逻辑、管理对象属性、控制操作顺序等方面的运算符。
条件(三元)运算符:? :
条件三元运算符 (? :
) 是在 JavaScript 代码中做决策的一种简洁方式。它因为是唯一接受三个操作数的运算符而得名。这个条件运算符的语法如下:
jsx
条件 ? 如果为真执行的表达式 : 如果为假执行的表达式
该运算符首先评估条件。如果条件为 true
,则执行 `如果为真执行的表达
式,如果条件为
false,则执行
如果为假执行的表达式`:
jsx
const age = 15;
const status = age >= 18 ? '成年人' : '未成年人'; // '未成年人'
在上面的代码中,三元运算符检查 age
是否大于或等于 18。由于 age
是 15,条件求值为 false
,因此将 '未成年人'
分配给 status
变量。
这个运算符可以是一种方便的方式来编写简洁的 if-else 语句,但如果过度使用或在复杂条件中使用,可能会使代码难以阅读。
扩展运算符:...
扩展运算符 (...
) 允许在期望零个或多个参数或元素的位置上展开可迭代对象(如数组或字符串)。它可以用于函数调用、数组字面量和对象字面量中:
jsx
// 在函数调用中
const numbers = [1, 2, 3];
console.log(...numbers); // 1 2 3
// 在数组字面量中
const moreNumbers = [4, 5, ...numbers];
console.log(moreNumbers); // [4, 5, 1, 2, 3]
// 在对象字面量中
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log(obj2); // { a: 1, b: 2, c: 3 }
扩展运算符可以用于创建数组或对象的副本、连接数组或将数组的元素作为参数传递给函数。
逗号运算符:,
逗号运算符 (,
) 允许在序列中评估多个表达式,并返回最后一个表达式的结果。这些表达式从左到右进行评估。
当我们需要在只允许一个表达式的位置包含多个表达式时,逗号运算符特别有用,例如在 for 循环的初始化或更新部分:
jsx
for(let i = 0, j = 10; i <= 10; i++, j--) {
console.log(`i: ${i}, j: ${j}`);
}
上面的代码中,逗号运算符用于在 for 循环中声明和更新两个变量 (i
和 j
)。
可选链运算符:?.
可选链 是 JavaScript 的一个相对较新的特性(截止到 ES2020),它简化了访问对象深层嵌套属性的过程。它通过提供一种方式来尝试检索属性值,而无需明确检查链中的每个引用是否存在,从而帮助我们编写更干净、更安全的代码:
jsx
const user = {
name: 'Jim',
address: {
street: '123 Hochstraße',
city: 'Munich'
}
};
user?.address?.city // 'Munich'
user?.contacts?.email // undefined
在上面的示例中,user?.address?.city
尝试访问 user.address
的 city
属性,如果 user
和 user.address
都存在,则返回 city
的值。否则,它将返回 undefined
。这种方法避免了 JavaScript 中的一个常见陷阱,即尝试访问 undefined
或 null
的属性会导致 TypeError
:
jsx
user.contacts.email // Uncaught TypeError: Cannot read properties of undefined (reading 'email')
在可选链之前,JavaScript 开发人员不得不使用冗长的条件逻辑来避免此类错误:
jsx
let email;
if (user && user.contacts) {
email = user.contacts.email;
} else {
email = '未指定';
}
email // '未指定'
管道运算符:|>
管道运算符 (|>
) 旨在提高代码的可读性,该代码否则将使用嵌套函数调用编写。它允许我们获取一个表达式的结果并将其传递给下一个表达式。这在我们对值应用一系列转换时特别有用:
jsx
const result = exclaim(capitalize(doubleSay('hello')));
console.log(result); //=> 'HELLO, HELLO!'
使用管道运算符,我们可以像这样重写这段代码:
jsx
const result = 'hello'
|> doubleSay
|> capitalize
|> exclaim;
console.log(result); //=> 'HELLO, HELLO!'
分组运算符:()
JavaScript 中的分组运算符 (()
) 用于更改表达式中评估的优先级顺序。这个运算符不对其值执行任何操作,但它控制了表达式中计算的顺序。
例如,乘法和除法的优先级高于加法和减法。这意味着,在表达式 2 + 3 * 4
中,首先执行乘法,得到 2 + 12
,然后执行加法,得到 14
。
如果我们想改变操作顺序,可以使用分组运算符。例如,在前面的示例中,如果我们希望加法在乘法之前执行,可以编写 (2 + 3) * 4
。在这种情况下,首先执行加法,得到 5 * 4
,然后执行乘法,得到 20
。
分组运算符允许我们确保按照我们打算的顺序执行操作,这在更复杂的数学或逻辑表达式中非常重要。
结论
在本文中,我们深入研究了 JavaScript 运算符的广泛且有时复杂的世界。这些运算符使我们能够操作数据、控制程序流程并进行复杂的计算。
了解这些运算符不仅仅是一种学术性的练习;它是一种实际的技能,可以提高我们编写和理解 JavaScript 代码的能力。
还记得我们一开始提到的令人困惑的代码段吗?让我们重新看一下,看看它现在是否更容易理解:
jsx
const x = 10, y = 20, z = 30;
console.log((x > y ? x : y) > z ? (x > y ? x : y) : z);
用通俗的语言来说,这段代码找到了三个数字 x
、y
和 z
中的最大值。
它的工作原理如下:
- 表达式
(x > y ? x : y)
检查x
是否大于y
。 - 如果
x
大于y
,它返回x
;否则,返回y
。换句话说,它获取x
和y
的最大值。 - 将这个结果(
x
和y
的最大值)与z
使用>
运算符进行比较。 - 如果
x
和y
的最大值大于z
,则再次评估(x > y ? x : y)
。 - 在这个三元表达式中,再次比较
x
和y
,并返回较大的值。 - 否则,如果
z
大于x
和y
的最大值,那么z
就是这三个数字的最大值,并将其返回。
嵌套三元运算符这样不容易阅读,可以使用 Math.max
更好地表达这个计算:
jsx
Math.max(x, y, z) // 30
尽管如此,理解 JavaScript 运算符的工作方式就像学习一门新语言的语法。起初可能有挑战,但一旦掌握了基础,就可以轻松地构建复杂的表达式,愉快的去装逼了。