JavaScript语言基础之操作符

本文最后更新于 2025年7月30日 下午

参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators

ECMA-262 描述了一组可用于操作数据值的操作符,包括数学操作符(如加、减)、位操作符、关系操作符和相等操作符等。ECMAScript 中的操作符是独特的,因为它们可用于各种值,包括字符串、数值、布尔值,甚至还有对象。在应用给对象时,操作符通常会调用 valueOf()toString() 方法来取得可以计算的值。

概述

注:

  1. 期待操作符类型中的lvallvalue(左值),意思是“一个可以合法地出现在赋值表达式左侧的表达式”。在 JavaScript 中,变量、对象元素、数组元素都是“左值”。
  2. 下表的操作符是按照优先级从高到低的顺序排列的,其中的横线分组了相同优先级的操作符。
  3. “左”表示结合性为从左到右,“右”表示结合性为从右到左。


JavaScript操作符

操作数个数

操作符可以按照它们期待的操作数个数(参数数量)来分类。

多数的 JavaScript 操作符都是二元操作符,可以将两个表达式组合成一个更复杂的表达式。换句话说,这些操作符期待两个操作数。

同时,JavaScript 也支持一元操作符,这些操作符将一个表达式转换为另一个更复杂的表达式。

最后,JavaScript 也支持一个三元操作符,即条件操作符,用于将三个表达式组合为一个表达式。

操作数与结果类型

有些操作数用于任何类型的数值,但是多数操作符期待自己的操作数是某种特定类型,而且多数操作符也返回(或求值为)特定类型的值,具体如上表。

JavaScript 操作符通常按照需要转换操作数的类型。如乘法操作符期待数值参数,但表达式'3' * '5'之所以合法,是因为 JavaScript 可以把操作数转化为数值,且其结果是数值 15 而不是字符串 15。

也要记住,每个 JavaScript 值要么是“真值”要么是“假值”,因此期待布尔值的操作符可以用于任何类型的操作符。

有些操作符的行为会因为操作数类型的不同而不同。最明显的就是加法操作符,其可以把数值加起来,也可以拼接字符串;还有比较操作符根据操作数类型会俺早数值顺序或字母表顺序比较。

操作符副效应

大部分如2 * 3这样的简单表达式求值不会影响程序状态,程序后续的计算也不会北这个求值所影响。

但是有些表达式是有副效应的,即对它们求值可能影响将来求值的结果。赋值操作符就是一例,把一个值赋给变量或属性,会改变后续使用该变量或属性的表达式的值。同理,递增和递减操作符也有副效应。此外,delete操作符的副效应在于删除属性类似于(但不同于)给属性赋值undefined

其它的操作符都没有副效应,但函数调用和对象创建表达式是否有副效应,取决于函数和构造函数体内是否使用了有副效应的操作符。

操作符优先级

靠近表格顶部的操作符咸鱼靠近表格底部的操作符先执行。

w = x + y * z,其中,乘法计算先与加法执行,最后赋值给左侧的变量。不过,具体的执行顺序可以由圆括号改写:``w = (x + y) * z`,这样便会先执行加法后进行乘法计算。

但是要注意,属性访问和调用表达式的优先级高于表格中的任何操作符。因为操作符要基于属性访问、数组索引和函数调用的结果执行,所以这些操作的优先级全部高于操作符。

如果不确定优先级,最简单的方法是使用圆括号明确求值顺序。最重要的规则在于:乘和除先于加和减执行,而赋值优先级很低,几乎总是最后执行。

JavaScript 新增的操作符并不总是符合这个优先级模式。比如,?? 操作符比 ||&& 优先级低,而实际上它相对于这两个操作符的优先级并没有定义,ES2020 要求在混用 ??||&& 时必须使用圆括号。类似地,** 相对于一元负值操作符的优先级也没有定义,因此在同时求负值和求幂时也必须使用圆括号。

操作符结合性

操作符结合性规定了相同优先级操作的执行顺序。左结合性意味着操作操作从做到右执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
w = x - y - z;
y = a ** (b ** c);
x = ~-y;
w = x = y = z;
q = a ? b : c ? d : e ? f : g;

// 等价于

w = x - y - z;
y = a ** (b ** c);
x = ~-y;
w = x = y = z;
q = a ? b : c ? d : e ? f : g;

这是因为,减操作符具有左结合性;而幂、一元、赋值、三元操作符具有右结合性。

求值顺序

操作符的优先级和结合性规定了复杂表达式中操作的执行顺序,但它们没有规定子表达式的求值顺序。JavaScript 始终严格按照从左到右的顺序对表达式求值。

例如,在表达式 w = x + y * z 中,子表达式 w 首先被求值,再对 x、y 和 z 求值。然后将 y 和 z 相乘,加到 x 上,再把结果赋值给表达式 w 表示的变量或属性。使用圆括号会改变执行顺序,但不会改变从左到右的求值顺序。

一元操作符

只操作一个值的操作符叫一元操作符,是 ECMAScript 中最简单的操作符。

递增和递减操作符

递增和递减操作符直接照搬自 C 语言,但有两个版本:前缀版和后缀版。顾名思义,前缀版就是位于要操作的变量前头,后缀版就是位于要操作的变量后头。

  • 前缀版:在跟其他操作混合时,变量的值在语句被求值之前改变(先递增递减后运算语句)
  • 后缀版:在跟其他操作混合时,变量的值在语句被求值之后发生(先运算语句后递增递减)
1
2
3
4
5
6
7
8
9
10
11
12
let age1 = (age2 = 29);
// 前缀版
++age1;
--age1;
// 后缀版
age1++;
age1--;

console.log(--age1 + 2); // 30
console.log(age2-- + 2); // 31
console.log(age1); // 28
console.log(age2); // 28

这 4 个操作符可作用于任何值,整数、字符串、布尔值、浮点值,甚至对象都可。递增和递减操作符遵循如下规则:

  • 数值,直接改变后返回
  • 布尔值,true 转换为 1,false 转换为 0,再应用改变,变量类型从布尔值变成数值
  • 字符串,如果是有效的数值形式,则转换为数值再应用改变;如果非有效的数值形式,则将变量的值设置为 NaN ,变量类型从字符串变成数值
  • 对象,则调用其 valueOf() 方法取得可以操作的值。对得到的值应用上述规则。如果是 NaN,则调用 toString() 并再次应用其他规则

一元加和减操作符

它们在 ECMAScript 中跟在高中数学中的用途一样。

一元加由一个加号(+)表示,放在变量前头,对数值没有任何影响。如果将一元加应用到非数值,则会执行与使用 Number() 转型函数一样的类型转换:布尔值 falsetrue 转换为 0 和 1,字符串根据特殊规则进行解析,对象会调用它们的 valueOf()toString()
方法以得到可以转换的值。

一元减由一个减号(-)表示,放在变量前头,用于把数值变成负值。如果将一元减应用到非数值,一元减会遵循与一元加同样的规则,先进行转换然后取负值。

位操作符

下面介绍的操作符用于数值的底层操作,也就是操作内存中表示数据的比特(位)。

布尔操作符

对于编程语言来说,布尔操作符跟相等操作符同样重要。如果没有能力测试两个值的关系,那么像 if-else 和循环这样的语句也没什么用。布尔操作符一共有 3 个:逻辑非(!)、逻辑与(&&)和逻辑或(||)。

逻辑非(!

可应用给 ECMAScript 中的任何值。这个操作符始终返回布尔值,无论应用到的是什么数据类型。逻辑非操作符首先将操作数转换为布尔值,然后再对其取反,遵循如下规则:

  • 如果操作数是对象,则返回 false
  • 如果操作数是符号,则返回 false
  • 如果操作数是空字符串,则返回 true
  • 如果操作数是非空字符串,则返回 false
  • 如果操作数是数值 0,则返回 true
  • 如果操作数是非 0 数值(包括 Infinity),则返回 false
  • 如果操作数是 null,则返回 true
  • 如果操作数是 NaN,则返回 true
  • 如果操作数是 undefined,则返回 true

逻辑非操作符也可以用于把任意值转换为布尔值。同时使用两个叹号(!!),相当于调用转型函数 Boolean()。无论操作数是什么类型,第一个叹号总会返回布尔值。第二个叹号对该布尔值取反,从而给出变量真正对应的布尔值。结果与使用 Boolean() 函数一样。

逻辑与(&&

逻辑与操作符可用于任何类型的操作数。如果有操作数不是布尔值,则逻辑与并不一定会返回布尔值,而是遵循如下规则:

  • 如果有一个操作数是 null,则返回 null
  • 如果有一个操作数是 NaN,则返回 NaN
  • 如果有一个操作数是 undefined,则返回 undefined
  • 如果第一个操作数是对象,则返回第二个操作数
  • 如果第二个操作数是对象,则只第一个操作数求值为 true 才会返回该对象
  • 如果两个操作数都是对象,则返回第二个操作数

逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值。对逻辑与操作符来说,如果第一个操作数是 false,那么无论第二个操作数是什么值,结果也不可能等于 true

逻辑或(||

与逻辑与类似,如果有一个操作数不是布尔值,那么逻辑或操作符也不一定返回布尔值。它遵循如下规则

  • 如果两个操作数都是 null,则返回 null
  • 如果两个操作数都是 NaN,则返回 NaN
  • 如果两个操作数都是 undefined,则返回 undefined
  • 如果第一个操作数求值为 false,则返回第二个操作数
  • 如果第一个操作数是对象,则返回第一个操作数
  • 如果两个操作数都是对象,则返回第一个操作数

同样与逻辑与类似,逻辑或操作符也具有短路的特性。第一个操作数求值为 true,第二个操作数就不会再被求值。

乘性操作符

ECMAScript 定义了 3 个乘性操作符:乘法、除法和取模。这些操作符跟它们在 Java、C 语言及 Perl 中对应的操作符作用一样,但在处理非数值时,它们也会包含一些自动的类型转换。如果乘性操作符有不是数值的操作数,则该操作数会在后台被使用 Number() 转型函数转换为数值。

乘法操作符(*

用于计算两个数值的乘积。乘法操作符在处理特殊值时也有一些特殊的行为:

  • 若操作数都是数值,则执行常规的乘法运算,即两个正值相乘是正值,两个负值相乘也是正值,正负符号不同的值相乘得到负值。如果 ECMAScript 不能表示乘积,则返回 Infinity 或-Infinity
  • 若有任一操作数是 NaN,则返回 NaN
  • 若是 Infinity 乘以 0,则返回 NaN
  • 若是 Infinity 乘以非 0 的有限数值,则根据第二个操作数的符号返回 Infinity 或 -Infinity
  • 若是 Infinity 乘以 Infinity,则返回 Infinity
  • 若有不是数值的操作数,则先用 Number() 将其转换为数值,然后再应用上述规则

除法操作符(/

用于计算第一个操作数除以第二个操作数的商。跟乘法操作符一样,除法操作符针对特殊值也有一些特殊的行为:

  • 如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正值,符号不同的值相除得到负值。如果 ECMAScript 不能表示商,则返回 Infinity 或 -Infinity。
  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 Infinity 除以 Infinity,则返回 NaN
  • 如果是 0 除以 0,则返回 NaN
  • 如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或 -Infinity
  • 如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或 -Infinity
  • 如果有不是数值的操作数,则先用 Number() 函数将其转换为数值,然后再用上述规则

取模操作符(%

与其他乘性操作符一样,取模操作符对特殊值也有一些特殊的行为:

  • 如果操作数是数值,则执行常规除法运算,返回余数
  • 如果被除数是无限值,除数是有限值,则返回 NaN
  • 如果被除数是有限值,除数是 0,则返回 NaN
  • 如果是 Infinity 除以 Infinity,则返回 NaN
  • 如果被除数是有限值,除数是无限值,则返回被除数
  • 如果被除数是 0,除数不是 0,则返回 0
  • 如果有不是数值的操作数,则先用 Number() 函数将其转换为数值,然后再用上述规则

指数操作符

ECMAScript 7 新增了指数操作符,Math.pow() 现在有了自己的操作符 **,两者计算结果一样。
而且,指数操作符也有自己的指数赋值操作符 **=,该操作符执行指数运算和结果的赋值操作。

1
2
3
4
5
6
console.log(Math.pow(16, 0.5)); // 4
console.log(16 ** 0.5); // 4

let squared = 3;
squared **= 2;
console.log(squared); // 9

加性操作符

加法操作符(+

如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:

  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 Infinity 加 Infinity,则返回 Infinity
  • 如果是 -Infinity 加 -Infinity,则返回 -Infinity
  • 如果是 Infinity 加 -Infinity,则返回 NaN
  • 如果是 +0 加 +0,则返回 +0
  • 如果是 -0 加 +0,则返回 +0
  • 如果是 -0 加 -0,则返回 -0

不过,如果有一个操作数是字符串,则要应用如下规则:

  • 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面
  • 如果只有一个操作数是字符串,则将另一个操作数转为字符串,再将两个字符串拼接在一起

如果有任一操作数是对象、数值或布尔值,则调用它们 toString() 方法以获取字符串,然后再应用关于字符串的规则。对于 undefinednull,则调用 String() 函数,分别获取 undefinednull

ECMAScript 中最常犯的一个错误,就是忽略加法操作中涉及的数据类型。

减法操作符(-

减法操作符也有一组规则用于处理 ECMAScript 中不同类型之间的转换:

  • 如果两个操作数都是数值,则执行数学减法运算并返回结果
  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 Infinity 减 Infinity,则返回 NaN
  • 如果是 -Infinity 减-Infinity,则返回 NaN
  • 如果是 Infinity 减-Infinity,则返回 Infinity
  • 如果是 -Infinity 减 Infinity,则返回 -Infinity
  • 如果是 +0 减 +0,则返回 +0
  • 如果是 +0 减 -0,则返回 -0
  • 如果是 -0 减 -0,则返回 +0
  • 若有任一操作数是字符串、布尔值、nullundefined,则先使用 Number() 将其转为数值,然后再根据前面的规则执行数学运算。若转换结果是 NaN,则结果是 NaN
  • 若有任一操作数是对象,则调用其 valueOf() 方法取得表示它的数值。如果该值是 NaN,则计算的结果是 NaN。如果对象没有 valueOf() 方法,则调用其 toString() 方法,然后再将得到的字符串转换为数值

关系操作符

关系操作符执行比较两个值的操作。

包括小于(<)、大于(>)、小于等于(<=)、大于等于(>=),这几个操作符返回布尔值。

与 ECMAScript 中的其他操作符一样,在将它们应用到不同数据类型时也会发生类型转换和其他行为:

  • 如果操作数都是数值,则执行数值比较
  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码
  • 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较
  • 如果有任一操作数是对象,则调用其 valueOf() 方法,取得结果后根据前面规则执行比较。如果没有 valueOf() 方法,则调用 toString() 方法,取得结果后根据前面规则比较
  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较

对字符串而言,关系操作符会比较字符串中对应字符的编码,而这些编码是数值。比较完后会返回布尔值。问题的关键在于大写字母的编码都小于小写字母的编码。如,字母 B 的编码是 66,字母 b 的编码是 98。要得到确实按字母顺序比较的结果,就必须把两者都转换为相同的大小写形式(全大写或全小写),然后再比较。

另一个奇怪的现象是在比较两个数值字符串的时候。在比较字符串 "23" < "3" 时返回 true。因为两个操作数都是字符串,所以会逐个比较它们字符编码(字符”2”的编码是 50,而字符”3”的编码是 51)。不过如果有一个操作数是数值,那么比较的结果就对了,"23" < 3 时返回 false。只要是数值和字符串比较,字符串就会先被转为数值,然后进行数值比较。对于数值字符串而言,这样能保证结果正确。

但如果字符串不能转换成数值呢?如果字符不能转换成任何有意义的数值只能转换为 NaN,有一个规则,即任何关系操作符在涉及比较 NaN 时都返回 false。在大多数比较的场景中,如果一个值不小于另一个值,那就一定大于或等于它。但在比较 NaN 时,无论是小于还是大于等于,比较的结果都会返回 false

相等操作符

判断两个变量是否相等是编程中最重要操作之一。

在比较字符串、数值和布尔值是否相等时过程都很直观。但是在比较两个对象是否相等时,情形就比较复杂。ECMAScript 中的相等和不相等操作符,原本在比较之前会执行类型转换,但是有人质疑这种转换是否应该发生。最终,ECMAScript 提供了两组操作符。第一组是等于和不等于,它们在比较之前执行转换。第二组是全等和不全等,它们在比较之前不执行转换。

等于和不等于

等于操作符(==),如果操作数相等,则会返回 true;不等于操作符(!=),如果两个操作数不相等,则会返回 true。这两个操作符会先进行类型转换(称为强制类型转换)再确定操作数是否相等。

在转换操作数的类型时,相等和不相等操作符遵循如下规则:

  • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1
  • 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等
  • 如果一个操作数是对象,另一个操作数不是对象,则调用对象的 valueOf() 方法取得其原始值,再根据前面规则进行比较

在进行比较时,这两个操作符会遵循如下规则:

  • nullundefined 相等
  • nullundefined 不能转换为其他类型的值再进行比较
  • 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true。否则,两者不相等

下表总结了一些特殊情况及比较的结果:

表达式 结果
null == undefined true
"NaN" == NaN false
5 == NaN false
NaN == NaN false
NaN != NaN true
false == 0 true
true == 1 true
true == 2 false
undefined == 0 false
null == 0 false
"5" == 5 true

全等和不全等

全等和不全等操作符与相等和不相等操作符类似,只不过在比较相等时不转换操作数。

全等操作符(===),只有两个操作数在不转换的前提下相等才返回 true。不全等操作(!==),只有两个操作数在不转换的前提下不相等才返回 true。由于相等和不相等操作符存在类型转换问题,推荐使用全等和不全等操作符。这样有助于在代码中保持数据类型的完整性

条件操作符

条件操作符是 ECMAScript 中用途最为广泛的操作符之一。

const variable = boolean_expression ? true_value : false_value;
如果 boolean_expressiontrue ,则赋 variable 值为 true_value;如果 boolean_expressionfalse,则赋 variable 值为 false_value

赋值操作符

简单赋值(=),将右手边的值赋给左手边的变量。复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。

每个数学操作符以及其他一些操作符都有对应的复合赋值操作符:

  • 乘后赋值(*=
  • 除后赋值(/=
  • 取模后赋值(%=
  • 加后赋值(+=
  • 减后赋值(-=
  • 指数赋值(**=
  • 左移后赋值(<<=
  • 右移后赋值(>>=
  • 无符号右移后赋值(>>>=

这些操作符仅仅是简写语法,使用它们不会提升性能。

逗号操作符

逗号操作符可以用来在一条语句中执行多个操作。

let num1 = 1, num2 = 2, num3 = 3;
在一条语句中同时声明多个变量是逗号操作符最常用的场景。

let num = (5, 1, 4, 8, 0); // num 的值为 0
也可以使用逗号操作符来辅助赋值,赋值时最终会返回表达式中最后一个值。逗号操作符的这种使用场景并不多见,但这种行为的确存在。


JavaScript语言基础之操作符
https://xuekeven.github.io/2023/01/24/JavaScript语言基础之操作符/
作者
Keven
发布于
2023年1月24日
更新于
2025年7月30日
许可协议