写给不耐烦程序员的 JavaScript(ES2022 版)
请支持本书:购买捐赠
(广告,请勿屏蔽。)

13 运算符



13.1 理解运算符

JavaScript 的运算符可能看起来很奇怪。通过以下两条规则,它们更容易理解

13.1.1 运算符将操作数强制转换为适当的类型

如果运算符获取的操作数类型不正确,它很少会抛出异常。相反,它会_强制_(自动转换)操作数,以便它可以使用它们。让我们看两个例子。

首先,乘法运算符只能处理数字。因此,它在计算结果之前将字符串转换为数字。

> '7' * '3'
21

其次,用于访问对象属性的方括号运算符 ([ ]) 只能处理字符串和符号。所有其他值都将强制转换为字符串

const obj = {};
obj['true'] = 123;

// Coerce true to the string 'true'
assert.equal(obj[true], 123);

13.1.2 大多数运算符仅适用于原始值

如前所述,大多数运算符仅适用于原始值。如果操作数是对象,它通常会被强制转换为原始值——例如

> [1,2,3] + [4,5,6]
'1,2,34,5,6'

为什么?加法运算符首先将其操作数强制转换为原始值

> String([1,2,3])
'1,2,3'
> String([4,5,6])
'4,5,6'

接下来,它连接两个字符串

> '1,2,3' + '4,5,6'
'1,2,34,5,6'

13.2 加法运算符 (+)

加法运算符在 JavaScript 中的工作方式如下

字符串模式允许我们使用 + 来组合字符串

> 'There are ' + 3 + ' items'
'There are 3 items'

数字模式意味着如果两个操作数都不是字符串(或成为字符串的对象),则所有内容都将强制转换为数字

> 4 + true
5

Number(true)1

13.3 赋值运算符

13.3.1 普通赋值运算符

普通赋值运算符用于更改存储位置

x = value; // assign to a previously declared variable
obj.propKey = value; // assign to a property
arr[index] = value; // assign to an Array element

变量声明中的初始化程序也可以看作一种赋值形式

const x = value;
let y = value;

13.3.2 复合赋值运算符

JavaScript 支持以下赋值运算符

13.3.2.1 逻辑赋值运算符 [ES2021]

逻辑赋值运算符与其他复合赋值运算符的工作方式不同

赋值运算符 等价于 仅在 a 为 时赋值
a ||= b a || (a = b) 假值
a &&= b a && (a = b) 真值
a ??= b a ?? (a = b) 空值

为什么 a ||= b 等价于以下表达式?

a || (a = b)

为什么不等价于这个表达式?

a = a || b

前一个表达式具有 短路 的优点:仅当 a 求值为 false 时才会计算赋值。因此,仅在必要时才执行赋值。相反,后一个表达式始终执行赋值。

有关 ??= 的更多信息,请参阅 §14.4.5 “空值合并赋值运算符 (??=) [ES2021]”

13.3.2.2 其余的复合赋值运算符

对于 || && ?? 以外的运算符 op,以下两种赋值方式是等效的

myvar op= value
myvar = myvar op value

例如,如果 op+,那么我们将得到运算符 +=,其工作方式如下。

let str = '';
str += '<b>';
str += 'Hello!';
str += '</b>';

assert.equal(str, '<b>Hello!</b>');

13.4 相等性:== 与 ===

JavaScript 有两种相等运算符:宽松相等 (==) 和严格相等 (===)。建议始终使用后者。

  ===== 的其他名称

13.4.1 宽松相等 (==!=)

宽松相等是 JavaScript 的怪癖之一。它经常强制转换操作数。其中一些强制转换是有意义的

> '123' == 123
true
> false == 0
true

其他的则不那么有意义

> '' == 0
true

如果(且仅当!)另一个操作数是原始值时,对象才会被强制转换为原始值

> [1, 2, 3] == '1,2,3'
true
> ['1', '2', '3'] == '1,2,3'
true

如果两个操作数都是对象,则仅当它们是同一个对象时才相等

> [1, 2, 3] == ['1', '2', '3']
false
> [1, 2, 3] == [1, 2, 3]
false

> const arr = [1, 2, 3];
> arr == arr
true

最后,== 认为 undefinednull 相等

> undefined == null
true

13.4.2 严格相等 (===!==)

严格相等从不强制转换。只有当两个值的类型相同时,它们才相等。让我们回顾一下之前与 == 运算符的交互,看看 === 运算符会做什么

> false === 0
false
> '123' === 123
false

只有当另一个值是同一个对象时,对象才等于该值

> [1, 2, 3] === '1,2,3'
false
> ['1', '2', '3'] === '1,2,3'
false

> [1, 2, 3] === ['1', '2', '3']
false
> [1, 2, 3] === [1, 2, 3]
false

> const arr = [1, 2, 3];
> arr === arr
true

=== 运算符不认为 undefinednull 相等

> undefined === null
false

13.4.3 建议:始终使用严格相等

我建议始终使用 ===。它使您的代码更易于理解,并避免您不得不考虑 == 的怪癖。

让我们看看 == 的两个用例,以及我建议改为做什么。

13.4.3.1 == 的用例:与数字或字符串进行比较

== 允许您检查值 x 是否为数字或该数字的字符串形式——只需一次比较

if (x == 123) {
  // x is either 123 or '123'
}

我更喜欢以下两种替代方案中的任何一种

if (x === 123 || x === '123') ···
if (Number(x) === 123) ···

您也可以在第一次遇到 x 时将其转换为数字。

13.4.3.2 == 的用例:与 undefinednull 进行比较

== 的另一个用例是检查值 x 是否为 undefinednull

if (x == null) {
  // x is either null or undefined
}

这段代码的问题在于,您无法确定某人是有意这样写的,还是他们打错了字,本意是 === null

我更喜欢以下两种替代方案中的任何一种

if (x === undefined || x === null) ···
if (!x) ···

第二种替代方案的缺点是它接受 undefinednull 以外的值,但这是 JavaScript 中一种既定的模式(将在 §15.3 “基于真值的 존재 확인” 中详细解释)。

以下三个条件也大致等效

if (x != null) ···
if (x !== undefined && x !== null) ···
if (x) ···

13.4.4 比 === 更严格:Object.is()

方法 Object.is() 比较两个值

> Object.is(123, 123)
true
> Object.is(123, '123')
false

它甚至比 === 更严格。例如,它认为 NaN涉及数字的计算的错误值)等于自身

> Object.is(NaN, NaN)
true
> NaN === NaN
false

这偶尔会有用。例如,您可以使用它来实现 Array 方法 .indexOf() 的改进版本

const myIndexOf = (arr, elem) => {
  return arr.findIndex(x => Object.is(x, elem));
};

myIndexOf() 在数组中查找 NaN,而 .indexOf() 则不会

> myIndexOf([0,NaN,2], NaN)
1
> [0,NaN,2].indexOf(NaN)
-1

结果 -1 表示 .indexOf() 在数组中找不到其参数。

13.5 排序运算符

表 3:JavaScript 的排序运算符。
运算符 名称
< 小于
<= 小于或等于
> 大于
>= 大于或等于

JavaScript 的排序运算符(表 3)适用于数字和字符串

> 5 >= 2
true
> 'bar' < 'foo'
true

<=>= 基于严格相等。

  排序运算符不适用于人类语言

排序运算符不适用于比较人类语言中的文本,例如,涉及大写或重音符号时。详细信息在 §20.6 “比较字符串” 中解释。

13.6 其他各种运算符

以下运算符在本的其他部分介绍

接下来的两小节 将讨论两个很少使用的运算符。

13.6.1 逗号运算符

逗号运算符有两个操作数,它会计算两个操作数并返回第二个操作数

> 'a', 'b'
'b'

有关此运算符的更多信息,请参阅 _Speaking JavaScript_

13.6.2 void 运算符

void 运算符计算其操作数并返回 undefined

> void (3 + 2)
undefined

有关此运算符的更多信息,请参阅 _Speaking JavaScript_

  测验

请参阅 测验应用程序