10.运算符

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

最后,==认为undefinednull相等:

> 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

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

> 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 ==的用例:与undefinednull比较

==的另一个用例是检查值xundefined还是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 各种其他运算符

  • 逗号运算符a, b
  • void运算符void 0
  • 布尔运算符,字符串,数字,对象的运算符:在本书的其他地方介绍。
下一节:许多编程语言都有一个名为null的“非值”。它表示变量当前未指向对象。例如,尚未初始化时。相比之下,JavaScript 有两个:undefined和null。