表达式和运算符
本章介绍 JavaScript 的表达式和运算符,包括赋值、比较、算术、位、逻辑、字符串、三元等。
从宏观层面看,**表达式**是一个有效的代码单元,它能解析为一个值。表达式有两种类型:有副作用的(例如赋值)和纯粹**求值**的。
表达式 `x = 7` 是第一种类型的示例。此表达式使用 `=` **运算符**将值七赋给变量 `x`。表达式本身求值为 `7`。
表达式 `3 + 4` 是第二种类型的示例。此表达式使用 `+` 运算符将 `3` 和 `4` 相加并产生一个值 `7`。然而,如果它最终不是更大结构(例如 `const z = 3 + 4` 这样的变量声明)的一部分,它的结果将立即被丢弃——这通常是程序员的错误,因为求值不会产生任何效果。
正如以上示例所示,所有复杂表达式都由**运算符**连接,例如 `=` 和 `+`。在本节中,我们将介绍以下运算符:
这些运算符连接由更高优先级运算符或基本表达式之一组成的**操作数**。您还可以在参考资料中找到运算符和表达式的完整详细列表。
运算符的**优先级**决定了在求值表达式时它们的应用顺序。例如:
const x = 1 + 2 * 3;
const y = 2 * 3 + 1;
尽管 `*` 和 `+` 的顺序不同,但两个表达式都将得到 `7`,因为 `*` 的优先级高于 `+`,所以 `*` 连接的表达式将始终首先求值。您可以通过使用括号(它创建了一个分组表达式——基本表达式)来覆盖运算符优先级。要查看完整的运算符优先级表以及各种注意事项,请参阅运算符优先级参考页面。
JavaScript 既有**二元**运算符和**一元**运算符,还有一个特殊的三元运算符,即条件运算符。二元运算符需要两个操作数,一个在运算符之前,一个在运算符之后:
operand1 operator operand2
例如,`3 + 4` 或 `x * y`。这种形式称为**中缀**二元运算符,因为运算符位于两个操作数之间。JavaScript 中的所有二元运算符都是中缀的。
一元运算符需要一个操作数,可以在运算符之前或之后:
operator operand operand operator
例如,`x++` 或 `++x`。`运算符 操作数` 形式称为**前缀**一元运算符,`操作数 运算符` 形式称为**后缀**一元运算符。`++` 和 `--` 是 JavaScript 中唯一的后缀运算符——所有其他运算符,如 `!`、`typeof` 等都是前缀的。
赋值运算符
赋值运算符根据其右操作数的值,将其值赋给左操作数。简单的赋值运算符是等号(`=`),它将右操作数的值赋给左操作数。也就是说,`x = f()` 是一个赋值表达式,将 `f()` 的值赋给 `x`。
还有复合赋值运算符,它们是下表中列出的操作的简写:
| 名称 | 简写运算符 | 含义 |
|---|---|---|
| 赋值 | x = f() |
x = f() |
| 加法赋值 | x += f() |
x = x + f() |
| 减法赋值 | x -= f() |
x = x - f() |
| 乘法赋值 | x *= f() |
x = x * f() |
| 除法赋值 | x /= f() |
x = x / f() |
| 取余赋值 | x %= f() |
x = x % f() |
| 指数赋值 | x **= f() |
x = x ** f() |
| 左移赋值 | x <<= f() |
x = x << f() |
| 右移赋值 | x >>= f() |
x = x >> f() |
| 无符号右移赋值 | x >>>= f() |
x = x >>> f() |
| 按位与赋值 | x &= f() |
x = x & f() |
| 按位异或赋值 | x ^= f() |
x = x ^ f() |
| 按位或赋值 | x |= f() |
x = x | f() |
| 逻辑与赋值 | x &&= f() |
x && (x = f()) |
| 逻辑或赋值 | x ||= f() |
x || (x = f()) |
| 空值合并赋值 | x ??= f() |
x ?? (x = f()) |
赋值给属性
如果表达式求值为一个对象,那么赋值表达式的左侧可以对该表达式的属性进行赋值。例如:
const obj = {};
obj.x = 3;
console.log(obj.x); // Prints 3.
console.log(obj); // Prints { x: 3 }.
const key = "y";
obj[key] = 5;
console.log(obj[key]); // Prints 5.
console.log(obj); // Prints { x: 3, y: 5 }.
有关对象的更多信息,请阅读使用对象。
如果表达式未求值为对象,则对该表达式的属性赋值将不执行赋值:
const val = 0;
val.x = 3;
console.log(val.x); // Prints undefined.
console.log(val); // Prints 0.
在严格模式下,上述代码会抛出错误,因为无法为原始值分配属性。
对不可修改的属性或没有属性的表达式(`null` 或 `undefined`)的属性赋值是错误的。
解构
对于更复杂的赋值,解构语法是 JavaScript 表达式,它允许使用与数组和对象字面量构造镜像的语法从数组或对象中提取数据。
不使用解构,从数组和对象中提取值需要多个语句:
const foo = ["one", "two", "three"];
const one = foo[0];
const two = foo[1];
const three = foo[2];
使用解构,您可以通过一个语句将多个值提取到不同的变量中:
const [one, two, three] = foo;
求值与嵌套
通常,赋值用于变量声明(即与 `const`、`let` 或 `var` 结合使用)或作为独立语句。
// Declares a variable x and initializes it to the result of f().
// The result of the x = f() assignment expression is discarded.
let x = f();
x = g(); // Reassigns the variable x to the result of g().
然而,与其他表达式一样,`x = f()` 等赋值表达式会求值为一个结果值。尽管此结果值通常不被使用,但它可以被另一个表达式使用。
链式赋值或在其他表达式中嵌套赋值可能导致意外行为。因此,一些 JavaScript 样式指南不鼓励链式或嵌套赋值。尽管如此,赋值链和嵌套有时可能会发生,因此理解它们的工作原理很重要。
通过链式或嵌套赋值表达式,其结果本身可以赋给另一个变量。它可以被记录,可以放入数组字面量或函数调用中,等等。
let x;
const y = (x = f()); // Or equivalently: const y = x = f();
console.log(y); // Logs the return value of the assignment x = f().
console.log(x = f()); // Logs the return value directly.
// An assignment expression can be nested in any place
// where expressions are generally allowed,
// such as array literals' elements or as function calls' arguments.
console.log([0, x = f(), 0]);
console.log(f(0, x = f(), 0));
求值结果与上表中“含义”列中 `=` 号右侧的表达式匹配。这意味着 `x = f()` 求值为 `f()` 的结果,`x += f()` 求值为结果和 `x + f()`,`x **= f()` 求值为结果幂 `x ** f()`,依此类推。
对于逻辑赋值,`x &&= f()`、`x ||= f()` 和 `x ??= f()`,返回值是逻辑操作的值,不带赋值,因此分别是 `x && f()`、`x || f()` 和 `x ?? f()`。
当这些表达式在没有括号或其他分组运算符(如数组字面量)的情况下链式使用时,赋值表达式**从右到左分组**(它们是右结合),但它们**从左到右求值**。
请注意,对于除 `=` 本身之外的所有赋值运算符,结果值始终基于操作**之前**操作数的值。
例如,假设已声明以下函数 `f` 和 `g` 以及变量 `x` 和 `y`:
function f() {
console.log("F!");
return 2;
}
function g() {
console.log("G!");
return 3;
}
let x, y;
考虑以下三个示例:
y = x = f();
y = [f(), x = g()];
x[f()] = g();
求值示例 1
`y = x = f()` 等同于 `y = (x = f())`,因为赋值运算符 `=` 是右结合的。但是,它从左到右求值:
- 赋值表达式 `y = x = f()` 开始求值。
- 此赋值左侧的 `y` 求值为对名为 `y` 的变量的引用。
- 赋值表达式 `x = f()` 开始求值。
- 此赋值左侧的 `x` 求值为对名为 `x` 的变量的引用。
- 函数调用 `f()` 向控制台打印“F!”,然后求值为数字 `2`。
- `f()` 的结果 `2` 被赋给 `x`。
- 赋值表达式 `x = f()` 现在已完成求值;其结果是 `x` 的新值,即 `2`。
- 该结果 `2` 反过来也被赋给 `y`。
- 赋值表达式 `y = x = f()` 现在已完成求值;其结果是 `y` 的新值——恰好是 `2`。`x` 和 `y` 都被赋值为 `2`,控制台已打印“F!”。
求值示例 2
`y = [ f(), x = g() ]` 也从左到右求值:
- 赋值表达式 `y = [ f(), x = g() ]` 开始求值。
- 此赋值左侧的 `y` 求值为对名为 `y` 的变量的引用。
- 内部数组字面量 `[ f(), x = g() ]` 开始求值。
- 函数调用 `f()` 向控制台打印“F!”,然后求值为数字 `2`。
- 赋值表达式 `x = g()` 开始求值。
- 此赋值左侧的 `x` 求值为对名为 `x` 的变量的引用。
- 函数调用 `g()` 向控制台打印“G!”,然后求值为数字 `3`。
- `g()` 的结果 `3` 被赋给 `x`。
- 赋值表达式 `x = g()` 现在已完成求值;其结果是 `x` 的新值,即 `3`。该结果 `3` 成为内部数组字面量中的下一个元素(在 `f()` 的 `2` 之后)。
- 内部数组字面量 `[ f(), x = g() ]` 现在已完成求值;其结果是一个包含两个值的数组:`[ 2, 3 ]`。
- 该数组 `[ 2, 3 ]` 现在被赋给 `y`。
- 赋值表达式 `y = [ f(), x = g() ]` 现在已完成求值;其结果是 `y` 的新值——恰好是 `[ 2, 3 ]`。`x` 现在被赋值为 `3`,`y` 现在被赋值为 `[ 2, 3 ]`,控制台已打印“F!”,然后打印“G!”。
求值示例 3
`x[f()] = g()` 也从左到右求值。(此示例假设 `x` 已被赋给某个对象。有关对象的更多信息,请阅读使用对象。)
- 赋值表达式 `x[f()] = g()` 开始求值。
- 此赋值左侧的属性访问 `x[f()]` 开始求值。
- 此属性访问中的 `x` 求值为对名为 `x` 的变量的引用。
- 然后函数调用 `f()` 向控制台打印“F!”,然后求值为数字 `2`。
- 此赋值中的属性访问 `x[f()]` 现在已完成求值;其结果是变量属性引用:`x[2]`。
- 然后函数调用 `g()` 向控制台打印“G!”,然后求值为数字 `3`。
- 该 `3` 现在被赋给 `x[2]`。(此步骤仅在 `x` 被赋给一个对象时才会成功。)
- 此赋值左侧的属性访问 `x[f()]` 开始求值。
- 赋值表达式 `x[f()] = g()` 现在已完成求值;其结果是 `x[2]` 的新值——恰好是 `3`。`x[2]` 现在被赋值为 `3`,控制台已打印“F!”,然后打印“G!”。
避免赋值链
链式赋值或在其他表达式中嵌套赋值可能导致意外行为。因此,不鼓励在同一语句中链式赋值。
特别是,将变量链放在 `const`、`let` 或 `var` 语句中通常**不起作用**。只有最外层/最左侧的变量会被声明;赋值链中的其他变量**不会**被 `const`/`let`/`var` 语句声明。例如:
const z = y = x = f();
此语句似乎声明了变量 `x`、`y` 和 `z`。然而,它实际上只声明了变量 `z`。`y` 和 `x` 要么是对不存在变量的无效引用(在严格模式下),要么更糟糕的是,在宽松模式下会隐式为 `x` 和 `y` 创建全局变量。
比较运算符
比较运算符比较其操作数,并根据比较结果是否为真返回一个逻辑值。操作数可以是数值、字符串、逻辑或对象值。字符串根据标准的字典顺序(使用 Unicode 值)进行比较。在大多数情况下,如果两个操作数类型不同,JavaScript 会尝试将它们转换为适合比较的类型。此行为通常会导致对操作数进行数值比较。比较中类型转换的唯一例外是 `===` 和 `!==` 运算符,它们执行严格相等和不等比较。这些运算符在检查相等性之前不会尝试将操作数转换为兼容类型。下表描述了基于此示例代码的比较运算符:
const var1 = 3;
const var2 = 4;
| 运算符 | 描述 | 返回 true 的示例 |
|---|---|---|
相等 (==) |
如果操作数相等,则返回 `true`。 |
3 == var1
3 == '3'
|
不相等 (!=) |
如果操作数不相等,则返回 `true`。 |
var1 != 4
|
严格相等 (===) |
如果操作数相等且类型相同,则返回 `true`。另请参见 `Object.is` 和 JS 中的相同性。 | 3 === var1 |
严格不相等 (!==) |
如果操作数类型相同但不相等,或类型不同,则返回 `true`。 |
var1 !== "3"
|
大于 (>) |
如果左操作数大于右操作数,则返回 `true`。 |
var2 > var1
|
大于或等于 (>=) |
如果左操作数大于或等于右操作数,则返回 `true`。 |
var2 >= var1
|
小于 (<) |
如果左操作数小于右操作数,则返回 `true`。 |
var1 < var2
|
小于或等于 (<=) |
如果左操作数小于或等于右操作数,则返回 `true`。 |
var1 <= var2
|
**注意:** `=>` 不是比较运算符,而是箭头函数的表示法。
算术运算符
算术运算符以数值(字面量或变量)作为其操作数,并返回一个单一的数值。标准算术运算符是加法(`+`)、减法(`-`)、乘法(`*`)和除法(`/`)。这些运算符与在大多数其他编程语言中用于浮点数时的工作方式相同(特别要注意,除以零会产生`Infinity`)。例如:
1 / 2; // 0.5
1 / 2 === 1.0 / 2.0; // this is true
除了标准算术运算(`+`、`-`、`*`、`/`)外,JavaScript 还提供下表中列出的算术运算符:
| 运算符 | 描述 | 示例 |
|---|---|---|
余数 (%) |
二元运算符。返回两个操作数相除的整数余数。 | 12 % 5 返回 2。 |
增量 (++) |
一元运算符。为其操作数加一。如果用作前缀运算符(`++x`),则返回其操作数加一后的值;如果用作后缀运算符(`x++`),则返回其操作数加一之前的值。 | 如果 `x` 为 3,则 `++x` 将 `x` 设置为 4 并返回 4,而 `x++` 返回 3,然后才将 `x` 设置为 4。 |
减量 (--) |
一元运算符。为其操作数减一。返回值与增量运算符类似。 | 如果 `x` 为 3,则 `--x` 将 `x` 设置为 2 并返回 2,而 `x--` 返回 3,然后才将 `x` 设置为 2。 |
一元负号 (-) |
一元运算符。返回其操作数的负值。 | 如果 `x` 为 3,则 `-x` 返回 -3。 |
一元加号 (+) |
一元运算符。尝试将操作数转换为数字,如果它不是数字的话。 |
`+"3"` 返回 `3`。 `+true` 返回 `1`。 |
幂运算符 (**) |
计算 `base` 的 `exponent` 次幂,即 `base^exponent`。 |
`2 ** 3` 返回 `8`。 `10 ** -1` 返回 `0.1`。 |
位运算符
位运算符将其操作数视为一组 32 位(零和一),而不是十进制、十六进制或八进制数。例如,十进制数九的二进制表示为 1001。位运算符对这些二进制表示执行操作,但它们返回标准的 JavaScript 数值。
下表总结了 JavaScript 的位运算符。
位逻辑运算符
从概念上讲,位逻辑运算符的工作方式如下:
-
操作数被转换为 32 位整数,并用一系列位(零和一)表示。超过 32 位的数字会丢弃其最高有效位。例如,以下超过 32 位的整数将被转换为 32 位整数:
Before: 1110 0110 1111 1010 0000 0000 0000 0110 0000 0000 0001 After: 1010 0000 0000 0000 0110 0000 0000 0001
-
第一个操作数中的每个位都与第二个操作数中的相应位配对:第一位与第一位,第二位与第二位,依此类推。
-
运算符应用于每对位,结果按位构造。
例如,九的二进制表示是 1001,十五的二进制表示是 1111。因此,当位运算符应用于这些值时,结果如下:
| 表达式 | 结果 | 二进制描述 |
|---|---|---|
15 & 9 |
9 |
1111 & 1001 = 1001 |
15 | 9 |
15 |
1111 | 1001 = 1111 |
15 ^ 9 |
6 |
1111 ^ 1001 = 0110 |
~15 |
-16 |
~ 0000 0000 … 0000 1111 = 1111 1111 … 1111 0000 |
~9 |
-10 |
~ 0000 0000 … 0000 1001 = 1111 1111 … 1111 0110 |
请注意,所有 32 位都使用按位非运算符进行了反转,并且最高有效位(最左侧的位)设置为 1 的值表示负数(补码表示)。`~x` 求值与 `-x - 1` 求值相同。
位移位运算符
位移位运算符需要两个操作数:第一个是要移位的数量,第二个指定第一个操作数要移位的位数。移位操作的方向由使用的运算符控制。
移位运算符将其操作数转换为 32 位整数,并返回 `Number` 或 `BigInt` 类型的结果:具体来说,如果左操作数的类型是 `BigInt`,它们返回 `BigInt`;否则,它们返回 `Number`。
移位运算符列于下表中。
| 运算符 | 描述 | 示例 |
|---|---|---|
|
左移 ( <<)
|
此运算符将第一个操作数向左移动指定的位数。左侧移出的多余位被丢弃。从右侧移入零位。 | `9<<2` 产生 36,因为 1001 向左移动 2 位变成 100100,即 36。 |
有符号右移 (>>) |
此运算符将第一个操作数向右移动指定的位数。右侧移出的多余位被丢弃。从左侧移入最左侧位的副本。 | `9>>2` 产生 2,因为 1001 向右移动 2 位变成 10,即 2。同样,`-9>>2` 产生 -3,因为符号被保留。 |
零填充右移 (>>>) |
此运算符将第一个操作数向右移动指定的位数。右侧移出的多余位被丢弃。从左侧移入零位。 | `19>>>2` 产生 4,因为 10011 向右移动 2 位变成 100,即 4。对于非负数,零填充右移和有符号右移产生相同的结果。 |
逻辑操作符
逻辑运算符通常与布尔(逻辑)值一起使用;当它们一起使用时,它们返回一个布尔值。然而,`&&`、`||` 和 `??` 运算符实际上返回其中一个指定操作数的值,因此如果这些运算符与非布尔值一起使用,它们可能会返回一个非布尔值。因此,它们更恰当地被称为“值选择运算符”。逻辑运算符在下表中描述。
| 运算符 | 用法 | 描述 |
|---|---|---|
逻辑与 (&&) |
expr1 && expr2 |
如果 `expr1` 可以转换为 `false`,则返回 `expr1`;否则,返回 `expr2`。因此,当与布尔值一起使用时,如果两个操作数都为 true,则 `&&` 返回 `true`;否则,返回 `false`。 |
逻辑或 (||) |
expr1 || expr2 |
如果 `expr1` 可以转换为 `true`,则返回 `expr1`;否则,返回 `expr2`。因此,当与布尔值一起使用时,如果任一操作数为 true,则 `||` 返回 `true`;如果两个都为 false,则返回 `false`。 |
空值合并运算符 (??) |
expr1 ?? expr2 |
如果 `expr1` 既不是 `null` 也不是 `undefined`,则返回 `expr1`;否则,返回 `expr2`。 |
逻辑非 (!) |
!expr |
如果其单个操作数可以转换为 `true`,则返回 `false`;否则,返回 `true`。 |
可以转换为 `false` 的表达式的例子包括那些求值为 `null`、`0`、`0n`、`NaN`、空字符串(`""`)或 `undefined` 的表达式。
以下代码展示了 `&&`(逻辑与)运算符的示例。
const a1 = true && true; // t && t returns true
const a2 = true && false; // t && f returns false
const a3 = false && true; // f && t returns false
const a4 = false && 3 === 4; // f && f returns false
const a5 = "Cat" && "Dog"; // t && t returns Dog
const a6 = false && "Cat"; // f && t returns false
const a7 = "Cat" && false; // t && f returns false
以下代码展示了 `||`(逻辑或)运算符的示例。
const o1 = true || true; // t || t returns true
const o2 = false || true; // f || t returns true
const o3 = true || false; // t || f returns true
const o4 = false || 3 === 4; // f || f returns false
const o5 = "Cat" || "Dog"; // t || t returns Cat
const o6 = false || "Cat"; // f || t returns Cat
const o7 = "Cat" || false; // t || f returns Cat
以下代码展示了 `??`(空值合并)运算符的示例。
const n1 = null ?? 1; // 1
const n2 = undefined ?? 2; // 2
const n3 = false ?? 3; // false
const n4 = 0 ?? 4; // 0
请注意 `??` 的工作方式类似于 `||`,但它仅在第一个表达式是“空值”(即`null`或`undefined`)时才返回第二个表达式。`??` 是比 `||` 更好的替代方案,用于为可能为 `null` 或 `undefined` 的值设置默认值,尤其是在像 `''` 或 `0` 这样的值是有效值且不应应用默认值时。
以下代码展示了 `!`(逻辑非)运算符的示例。
const n1 = !true; // !t returns false
const n2 = !false; // !f returns true
const n3 = !"Cat"; // !t returns false
短路求值
由于逻辑表达式从左到右求值,它们会根据以下规则进行“短路”求值测试:
- `falsy && anything` 短路求值为 falsy 值。
- `truthy || anything` 短路求值为 truthy 值。
- `nonNullish ?? anything` 短路求值为非空值。
逻辑规则保证这些求值始终正确。请注意,上述表达式中的*anything*部分未被求值,因此这样做的任何副作用都不会生效。
BigInt 运算符
大多数可用于数字之间的运算符也可用于 `BigInt` 值。
// BigInt addition
const a = 1n + 2n; // 3n
// Division with BigInts round towards zero
const b = 1n / 2n; // 0n
// Bitwise operations with BigInts do not truncate either side
const c = 40000000000000000n >> 2n; // 10000000000000000n
一个例外是无符号右移 (>>>),它未为 BigInt 值定义。这是因为 BigInt 没有固定宽度,因此严格来说它没有“最高位”。
const d = 8n >>> 2n; // TypeError: BigInts have no unsigned right shift, use >> instead
BigInt 和数字不能相互替换——您不能在计算中混合使用它们。
const a = 1n + 2; // TypeError: Cannot mix BigInt and other types
这是因为 BigInt 既不是数字的子集也不是超集。BigInt 在表示大整数时具有比数字更高的精度,但不能表示小数,因此任何一方的隐式转换都可能导致精度丢失。使用显式转换来表明您希望操作是数字操作还是 BigInt 操作。
const a = Number(1n) + 2; // 3
const b = 1n + BigInt(2); // 3n
您可以比较 BigInt 和数字。
const a = 1n > 2; // false
const b = 3 > 2n; // true
字符串运算符
除了可用于字符串值的比较运算符之外,连接运算符 (+) 将两个字符串值连接在一起,返回另一个字符串,该字符串是两个操作数字符串的并集。
例如,
console.log("my " + "string"); // console logs the string "my string".
简写赋值运算符 `+=` 也可以用于连接字符串。
例如,
let myString = "alpha";
myString += "bet"; // evaluates to "alphabet" and assigns this value to myString.
条件(三元)运算符
条件(三元)运算符是 JavaScript 中唯一接受三个操作数的运算符。该运算符根据条件可以取两个值之一。语法是:
condition ? val1 : val2
如果 `condition` 为真,则运算符的值为 `val1`。否则,其值为 `val2`。您可以在任何使用标准运算符的地方使用条件运算符。
例如,
const status = age >= 18 ? "adult" : "minor";
此语句在 `age` 大于或等于十八岁时将值“adult”赋给变量 `status`。否则,它将值“minor”赋给 `status`。
逗号运算符
逗号运算符 (`,`) 求值其两个操作数并返回最后一个操作数的值。此运算符主要用于 `for` 循环内部,以允许每次循环更新多个变量。在不必要的地方使用它是糟糕的风格。通常,可以而且应该使用两个单独的语句代替。
例如,如果 `a` 是一个每边有 10 个元素的二维数组,以下代码使用逗号运算符一次更新两个变量。该代码打印数组中对角线元素的值:
const x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const a = [x, x, x, x, x];
for (let i = 0, j = 9; i <= j; i++, j--) {
// ^
console.log(`a[${i}][${j}]= ${a[i][j]}`);
}
一元运算符
一元运算是只有一个操作数的运算。
delete
`delete` 运算符删除对象的属性。语法是:
delete object.property;
delete object[propertyKey];
delete objectName[index];
其中 `object` 是对象的名称,`property` 是现有属性,`propertyKey` 是指向现有属性的字符串或符号。
如果 `delete` 运算符成功,它将从对象中删除该属性。之后尝试访问它将得到 `undefined`。如果操作可能,`delete` 运算符返回 `true`;如果操作不可能,则返回 `false`。
delete Math.PI; // returns false (cannot delete non-configurable properties)
const myObj = { h: 4 };
delete myObj.h; // returns true (can delete user-defined properties)
删除数组元素
由于数组只是对象,从技术上讲,可以从它们中 `delete` 元素。然而,这被认为是一种不好的做法——尽量避免它。当您删除数组属性时,数组长度不受影响,其他元素也不会重新索引。为了实现这种行为,最好直接将元素覆盖为 `undefined` 值。要实际操作数组,请使用各种数组方法,例如`splice`。
typeof
`typeof` 运算符返回一个字符串,指示未求值操作数的类型。`operand` 是要返回其类型的字符串、变量、关键字或对象。括号是可选的。
假设您定义了以下变量:
const myFun = () => 5 + 2;
const shape = "round";
const size = 1;
const foo = ["Apple", "Mango", "Orange"];
const today = new Date();
`typeof` 运算符为这些变量返回以下结果:
typeof myFun; // returns "function"
typeof shape; // returns "string"
typeof size; // returns "number"
typeof foo; // returns "object"
typeof today; // returns "object"
typeof doesntExist; // returns "undefined"
对于关键字 `true` 和 `null`,`typeof` 运算符返回以下结果:
typeof true; // returns "boolean"
typeof null; // returns "object"
对于数字或字符串,`typeof` 运算符返回以下结果:
typeof 62; // returns "number"
typeof "Hello world"; // returns "string"
对于属性值,`typeof` 运算符返回属性包含的值的类型:
typeof document.lastModified; // returns "string"
typeof window.length; // returns "number"
typeof Math.LN2; // returns "number"
对于方法和函数,`typeof` 运算符返回以下结果:
typeof blur; // returns "function"
typeof parseInt; // returns "function"
typeof shape.split; // returns "function"
对于预定义对象,`typeof` 运算符返回以下结果:
typeof Date; // returns "function"
typeof Function; // returns "function"
typeof Math; // returns "object"
typeof Option; // returns "function"
typeof String; // returns "function"
void
`void` 运算符指定一个表达式要被求值而不返回一个值。`expression` 是一个要被求值的 JavaScript 表达式。围绕表达式的括号是可选的,但为了避免优先级问题,最好使用它们。
关系运算符
关系运算符比较其操作数,并根据比较结果是否为真返回一个布尔值。
in
`in` 运算符如果指定属性在指定对象中,则返回 `true`。语法是:
propNameOrNumber in objectName
其中 `propNameOrNumber` 是表示属性名或数组索引的字符串、数字或符号表达式,`objectName` 是对象的名称。
以下示例展示了 `in` 运算符的一些用法。
// Arrays
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // returns true
3 in trees; // returns true
6 in trees; // returns false
"bay" in trees; // returns false
// (you must specify the index number, not the value at that index)
"length" in trees; // returns true (length is an Array property)
// built-in objects
"PI" in Math; // returns true
const myString = new String("coral");
"length" in myString; // returns true
// Custom objects
const myCar = { make: "Honda", model: "Accord", year: 1998 };
"make" in myCar; // returns true
"model" in myCar; // returns true
instanceof
`instanceof` 运算符如果指定对象是指定对象类型,则返回 `true`。语法是:
object instanceof objectType
其中 `object` 是要针对 `objectType` 进行测试的对象,`objectType` 是表示类型的构造函数,例如 `Map` 或 `Array`。
当您需要在运行时确认对象的类型时,请使用 `instanceof`。例如,在捕获异常时,您可以根据抛出的异常类型分支到不同的异常处理代码。
例如,以下代码使用 `instanceof` 来确定 `obj` 是否是 `Map` 对象。由于 `obj` 是 `Map` 对象,因此 `if` 块内的语句将执行。
const obj = new Map();
if (obj instanceof Map) {
// statements to execute
}
基本表达式
所有运算符最终都作用于一个或多个基本表达式。这些基本表达式包括标识符和字面量,但也有其他几种类型。下面简要介绍它们,并在各自的参考部分详细描述其语义。
this
`this` 关键字通常在函数内部使用。一般来说,当函数作为方法附加到对象时,`this` 指的是调用该方法的对象。它就像一个传递给函数的隐藏参数。`this` 是一个求值为对象的表达式,因此您可以使用我们介绍的所有对象操作。
this["propertyName"];
this.propertyName;
doSomething(this);
例如,假设函数定义如下:
function getFullName() {
return `${this.firstName} ${this.lastName}`;
}
现在我们可以将这个函数附加到一个对象,它在调用时将使用该对象的属性:
const person1 = {
firstName: "Chris",
lastName: "Martin",
};
const person2 = {
firstName: "Chester",
lastName: "Bennington",
};
// Attach the same function
person1.getFullName = getFullName;
person2.getFullName = getFullName;
console.log(person1.getFullName()); // "Chris Martin"
console.log(person2.getFullName()); // "Chester Bennington"
分组运算符
分组运算符 `( )` 控制表达式中的求值优先级。例如,您可以覆盖先乘除后加减的默认顺序,先进行加法运算。
const a = 1;
const b = 2;
const c = 3;
// default precedence
a + b * c; // 7
// evaluated by default like this
a + (b * c); // 7
// now overriding precedence
// addition before multiplication
(a + b) * c; // 9
// which is equivalent to
a * c + b * c; // 9
属性访问器
属性访问器语法使用点符号或方括号符号获取对象的属性值。
object.property;
object["property"];
使用对象指南详细介绍了对象属性。
可选链
可选链语法 (`?.`) 在对象已定义且非 `null` 时执行链式操作,否则短路操作并返回 `undefined`。这允许您对可能为 `null` 或 `undefined` 的值进行操作而不会导致 `TypeError`。
maybeObject?.property;
maybeObject?.[property];
maybeFunction?.();
new
您可以使用`new` 运算符创建用户定义对象类型或内置对象类型的实例。使用 `new` 如下:
const objectName = new ObjectType(param1, param2, /* …, */ paramN);
super
`super` 关键字用于调用对象父级上的函数。例如,它在类中调用父构造函数时很有用。
super(args); // calls the parent constructor.
super.functionOnParent(args);