10.1 理解运算符
JavaScript 的运算符可能看起来很古怪。使用以下两个规则,它们更容易理解:
- 运算符将其操作数强制转换为适当的类型
- 大多数运算符只处理原始值
10.1.1 运算符将其操作数强制转换为适当的类型
如果运算符获取的操作数不具有正确的类型,则很少会抛出异常。相反,它强制转换 (自动转换)操作数,以便它可以使用它们。我们来看两个例子。
首先,乘法运算符只能用于数字。因此,它在计算结果之前将字符串转换为数字。
> '7' * '3'
21
其次,用于访问对象属性的方括号运算符([ ]
)只能处理字符串和符号。所有其他值都强制转换为字符串:
const obj = {};
obj['true'] = 123;
// Coerce true to the string 'true'
assert.equal(obj[true], 123);
10.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'复制ErrorOK!
接下来,它连接两个字符串:
> '1,2,3' + '4,5,6'
'1,2,34,5,6'
10.2 加法运算符(+
)
加法运算符在 JavaScript 中如下工作:
- 首先,它将两个操作数转换为原始值。然后它切换到以下两种模式之一:
- 字符串模式:如果两个原始值中的一个是字符串,则它将另一个转换为字符串,连接两个字符串并返回结果。
- 数字模式:否则,它将两个操作数转换为数字,将它们相加并返回结果。
字符串模式让我们使用+
来组合字符串:
> 'There are ' + 3 + ' items'
'There are 3 items'
数字模式意味着如果操作数都不是字符串(或字符串对象),那么所有内容都被强制转换为数字:
> 4 + true
5
Number(true)
是1
。
10.3 赋值运算符
10.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;
10.3.2 复合赋值运算符
给定运算符op
,以下两种赋值方式是等效的:
myvar op= value
myvar = myvar op value
例如,如果op
是+
,那么我们得到如下工作的运算符+=
。
let str = '';
str += '<b>';
str += 'Hello!';
str += '</b>';
10.3.3 所有复合赋值运算符的列表
- 算术运算符:
+ = -= *= /= %= **=
+=
也适用于字符串连接 - 按位运算符:
<<= >>= >>>= &= ^= |=
10.4 相等:==
与===
JavaScript 有两种相等运算符:松散相等(==
)和严格相等(===
)。建议总是使用后者。
10.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
最后,==
认为undefined
和null
相等:
> undefined == null
true
==
的其他名称
- 抽象相等比较是语言规范中
==
的正式名称。 - 双等 是它的另一个名字。
10.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
===
运算符不认为undefined
和null
相等:
> undefined === null
false
===
的另一个名称,三等 是===
的另一个名称。
10.4.3 建议:始终使用严格相等
我建议总是使用===
。它使您的代码更容易理解,并使您不必考虑==
的怪癖。让我们看看==
的两个用例以及我建议做的事情。
10.4.3.1 ==
的用例:比较数字或字符串
==
允许您检查值x
是数字还是作为字符串的数字 - 只需一次比较:
if (x == 123) {
// x is either 123 or '123'
}
我更喜欢以下两种选择之一:
if (x === 123 || x === '123') ···
if (Number(x) === 123) ···
您第一次遇到它时也可以将x
转换为数字。
10.4.3.2 ==
的用例:与undefined
或null
比较
==
的另一个用例是检查值x
是undefined
还是null
:
if (x == null) {
// x is either null or undefined
}
这段代码的问题在于,你无法确定是否有人打算以这种方式编写,或者是否他们输错了并且意思是=== null
。我更喜欢以下两种选择之一:
if (x === undefined || x === null) ···
if (x) ···
第二种选择比使用==
更加草率,但它在 JavaScript 中是一种成熟的模式(将在 12.布尔值 的章节中详细解释,我们在其中看到真实性和虚假性)。
10.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()
无法在 Array 中找到其参数。
10.5 顺序运算符
表 3:JavaScript 的顺序运算符
运算符 | 名称 |
---|---|
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
JavaScript 的顺序运算符(表 3)适用于数字和字符串:
> 5 >= 2
true
> 'bar' < 'foo'
true
=
基于严格相等。
顺序运算符不适合人类语言
顺序操作符不能很好地用于比较人类语言中的文本,例如,当涉及大写或口音时。有关详细信息,请参阅 16.字符串 的章节。
10.6 各种其他运算符
下一节:许多编程语言都有一个名为null的“非值”。它表示变量当前未指向对象。例如,尚未初始化时。相比之下,JavaScript 有两个:undefined和null。