运算符的优先级
JavaScript 各种运算符的优先级别(Operator Precedence)是不一样的。优先级高的运算符先执行,优先级低的运算符后执行。
4 + 5 * 6 // 34
上面的代码中,乘法运算符(
*
)的优先性高于加法运算符(
+
),所以先执行乘法,再执行加法,相当于下面这样。
4 + (5 * 6) // 34
如果多个运算符混写在一起,常常会导致令人困惑的代码。
var x = 1; var arr = []; var y = arr.length <= 0 || arr[0] === undefined ? x : arr[0];
上面代码中,变量
y
的值就很难看出来,因为这个表达式涉及5个运算符,到底谁的优先级最高,实在不容易记住。
根据语言规格,这五个运算符的优先级从高到低依次为:小于等于(
<=
)、严格相等(
===
)、或(
||
)、三元(
?:
)、等号(
=
)。因此上面的表达式,实际的运算顺序如下。
var y = ((arr.length <= 0) || (arr[0] === undefined)) ? x : arr[0];
记住所有运算符的优先级,是非常难的,也是没有必要的。
执行顺序优先级从高到低:
-
圆括号运算符:
()
,优先级最高 -
一元运算符:
++
、--
、!
-
算数运算符:先
*
、/
、%
,后+
、-
-
关系运算符:
<
、<=
、>
、>=
-
相等运算符:
==
、!=
、===
、!==
-
逻辑运算符:先
&&
,后||
-
三目运算符:
?
:
-
赋值运算符:
=
运算符 | 结合 | 描述 |
---|---|---|
.
、
[]
、
()
|
从左到右 | 字段访问、数组下标、函数调用以及表达式分组 |
++
、
--
、
-
、
~
、
!
、
delete
、
new
、
typeof
、
void
|
从右到左 | 一元运算符、返回数据类型、对象创建、未定义值 |
**
|
从右到左 | 幂运算 |
*
、
/
、
%
|
从左到右 | 乘法、除法、取模 |
+
、
-
、
+
|
从左到右 | 加法、减法、字符串连接 |
<<
、
>>
、
>>>
|
从左到右 | 移位 |
<
、
<=
、
>
、
>=
、
instanceof
|
从左到右 | 小于、小于等于、大于、大于等于、instanceof |
==
、
!=
、
===
、
!==
|
从左到右 | 等于、不等于、严格相等、非严格相等 |
&
|
从左到右 | 按位与 |
^
|
从左到右 | 按位异或 |
|
|
从左到右 | 按位或 |
&&
|
从左到右 | 逻辑与 |
||
|
从左到右 | 逻辑或 |
?
:
|
从左到右 | 三目运算 |
>=
、
oP=
|
从右到左 | 赋值、运算赋值 |
,
|
从左到右 | 多重求值 |
圆括号的作用
圆括号
(
)
可以用来提高运算的优先级,因为它的优先级是最高的,即圆括号中的表达式会第一个运算。
(4 + 5) * 6 // 54
上面代码中,由于使用了圆括号,加法会先于乘法执行。
运算符的优先级别十分繁杂,且都是硬性规定,因此建议总是使用圆括号,保证运算顺序清晰可读,这对代码的维护和除错至关重要。
顺便说一下,圆括号不是运算符,而是一种语法结构。它一共有两种用法:一种是把表达式放在圆括号之中,提升运算的优先级;另一种是跟在函数的后面,作用是调用函数。
注意,因为圆括号不是运算符,所以不具有求值作用,只改变运算的优先级。
var x = 1; (x) = 2;
上面代码的第二行,如果圆括号具有求值作用,那么就会变成
1 = 2
,这是会报错了。但是,上面的代码可以运行,这验证了圆括号只改变优先级,不会求值。
这也意味着,如果整个表达式都放在圆括号之中,那么不会有任何效果。
(expression) // 等同于 expression
函数放在圆括号中,会返回函数本身。如果圆括号紧跟在函数的后面,就表示调用函数。
function f() { return 1; } (f) // function f(){return 1;} f() // 1
上面代码中,函数放在圆括号之中会返回函数本身,圆括号跟在函数后面则是调用函数。
圆括号之中,只能放置表达式,如果将语句放在圆括号之中,就会报错。
(var a = 1) // SyntaxError: Unexpected token var
左结合与右结合
对于优先级别相同的运算符,同时出现的时候,就会有计算顺序的问题。
a OP b OP c
上面代码中,
OP
表示运算符。它可以有两种解释方式。
// 方式一 (a OP b) OP c // 方式二 a OP (b OP c)
上面的两种方式,得到的计算结果往往是不一样的。方式一是将左侧两个运算数结合在一起,采用这种解释方式的运算符,称为“左结合”(left-to-right associativity)运算符;方式二是将右侧两个运算数结合在一起,这样的运算符称为“右结合”运算符(right-to-left associativity)。
JavaScript 语言的大多数运算符是“左结合”,请看下面加法运算符的例子。
x + y + z // 引擎解释如下 (x + y) + z
上面代码中,
x
与
y
结合在一起,它们的预算结果再与
z
进行运算。
少数运算符是“右结合”,其中最主要的是赋值运算符(
=
)和三元条件运算符(
?:
)。
w = x = y = z; q = a ? b : c ? d : e ? f : g;
上面代码的解释方式如下。
w = (x = (y = z)); q = a ? b : (c ? d : (e ? f : g));
上面的两行代码,都是右侧的运算数结合在一起。
另外,指数运算符(
**
)也是右结合。
2 ** 3 ** 2 // 相当于 2 ** (3 ** 2) // 512