“JavaScript⾥的不少奇技淫巧,都来⾃于对运算符的灵活使⽤。”html
说到运算符的优先级,咱们每每会想到一张见过无数次却历来没背下来的表。由于没背下来, 因此每每会认为它很简单,只要拿不许的时候去看看就好。我曾经也是这么认为的,直到在一个明媚的下午,我对着这张遍,遇到了几个问题。我才发现我其实并无把它搞定。express
《如晴天,似雨天》但愿你也能有一个阳光明媚的下午,来解开心中的这些困惑。bash
咱们先看一下完整的JavaScript运算符优先级表(文章最后,移动端锚点连接很差用,手滑下去吧)框架
这张表说明了两个问题:ecmascript
问题: 1 || 1 ? 2 : 3 ;
答案:2
解析:||的优先级高
至关于: (1 || 1 )? 2 : 3
而不是: 1 || (1 ? 2 : 3 )
复制代码
问题:+function (){var a = b = 1;}();
console.log(b);
console.log(a);
答案:1 error
解析:赋值从右到左,var a = b = 1因此至关于
b = 1;
var a = b;
那有同窗可能会问,为何不是?
var b = 1;
var a = b;
还记得变量提高吗?var a = b = 1;在变量提高的时候,只会把a去声明,并不会执行赋值中的b。
因此要想把b也声明了就须要按照语法 var a=1 , b ;
复制代码
如今咱们仔细把优先级的题改一下ide
1 || fn() && fn()
复制代码
MDN上写的是优先级高的运算符最早被执行,咱们都知道 ||是短路的,后边不会执行。那么这个最早被执行的含义是什么呢?函数
问题:1 || fn() && fn()
答案:1 fn不会执行
解析:就是利用&&运算符的短路原理啊。
复制代码
讲到这有些同窗会以为很简单啊,就是这样啊,看到短路后边就不用算了啊。也有的同窗可能会有点懵,不是说好了, 优先级高的先被执行吗?明明&&的优先级高啊。哈哈,别吵吵,咱们一块儿看下一题。学习
问题:var a = 42;
var b = "foo";
var c = 0;
c || b ? a : b ; // 42
复制代码
刚才说短路的同窗可能会说仍是要参考优先级。刚才说优先级的同窗可能一脸懵逼,静静地不想说话。那么咱们开始今天的学习吧。ui
定义:运算符的优先级高先执行,并非真正的执行,而是更强的绑定。spa
咱们用上面来两个问题
1 || fn() && fn() // &&的优先级高,因此将后边的绑定
1 ||(fn() && fn()) // 因此至关于1 和(fn() && fn())的值去逻辑或
1 ||(fn() && fn()) // 咱们查表,逻辑或从左到右执行。
1 ||(fn() && fn()) // 左执行,1是真值,因此短路,后边不执行
复制代码
问题: var a = 42;
var b = "foo";
var c = 0;
c || b ? a : b ;
答案:42
解析:c || b ? a : b ; //查表 条件运算符权重是4,逻辑与符权重是6,因此逻辑与有更强的绑定
(c || b )? a : b ; //(c || b )至关于条件运算符里的条件
(c || b )? a : b ; //(c || b )值是0 ,因此值是 a
复制代码
好,咱们在作两题巩固一下
问题: var a = 5;
var b = 5;
var c = 5+a+++b;
[ a , c ]
答案: [6, 15]
解析: b = 5+a+++b; //查表 后置递增权重17 前置递增权重16
b = 5 +(a++)+ b; //++优先级更高,因此和绑定a绑定在一块儿
b = 5 +(a++)+ b; //根据语法后置递增先执行语句,后递增
b = 5 +(a++)+ b; //执行语句时a是5,因此b是15
b = 5 +(a++)+ b; //a在进行自增,获得6
复制代码
问题: var a = 5;
var b = 5;
var c = ++a-b;
[ a , c ]
答案: [6, 1]
解析: var c = ++a-b; //查表 前置递增权重和一元减权重都是16,从左往右执行
var c = ++a-b; //根据语法前置递增先递增,后执行语句 a = 6
var c = ++a-b; //执行语句时a是6,因此b是1
复制代码
看到这,同窗们可能恍然大悟,就这么回事啊。别急,咱们来看下一题。 要解决这个问题,须要咱们理解下一节的概念。
问题: var a = 42;
var b = "foo";
var c = 0;
a && b || c ? c || b ? a : c && b : a
复制代码
定义:运算符的关联性去定义表达式的处理方向
来,用题说话
问题:a && b && c 的执行顺序
解析:(1)两个运算符都是&&,权重同样。因此这个时候就要看关联性。
(2)查表 &&的关联性是从左到右
(3)因此表达式应该是 ( a && b ) && c
复制代码
问题:a ? b :c ? d : e 的执行顺序
解析:(1)两个运算符都是条件运算符,权重同样。因此这个时候就要看关联性。
(2)查表条件运算符的关联性是从右到左
(3)因此表达式应该是 a ? b :(c ? d : e )
复制代码
好了,如今咱们就能够轻松解决上面那个问题啦。
问题: var a = 42;
var b = "foo";
var c = 0;
a && b || c ? c || b ? a : c && b : a
答案: 42
解析:(a && b) || c ? c || b ? a :(c && b) : a //首先查表逻辑与权重是6最高
((a && b) || c) ? c || b ? a :(c && b) : a //而后是逻辑或
((a && b) || c) ? (c || b ? a :(c && b)) : a //两个条件运算符,权重同样。关联性从右到左
复制代码
啊、、有没有很开心 最后的最后,咱们来说一个释疑
哈哈,释疑顾名思义就是解释调疑惑的地方,那最好的办法就是加()。
若是你可以熟练运用优先级/关联的规则,你的代码能更简洁,许多框架都是这样写的,很是漂亮。
可是你要叫不许,那就加()吧,千万别逞能,美其名曰有助于代码的可阅读性。
优先级 | 运算类型 | 关联性 | 运算符 |
---|---|---|---|
20 | 圆括号 | n/a | ( … ) |
19 | 成员访问 | 从左到右 | … . … |
需计算的成员访问 | 从左到右 | … [ … ] | |
new (带参数列表) | n/a | new … ( … ) | |
函数调用 | 从左到右 | … ( … ) | |
18 | new (无参数列表) | 从右到左 | new … |
17 | 后置递增(运算符在后) | n/a | … ++ |
后置递减(运算符在后) | … -- | ||
16 | 逻辑非 | 从右到左 | ! … |
按位非 | ~ … | ||
一元加法 | + … | ||
一元减法 | - … | ||
前置递增 | ++ … | ||
前置递减 | -- … | ||
typeof | typeof … | ||
void | void … | ||
delete | delete … | ||
await | await … | ||
15 | 幂 | 从右到左 | … ** … |
14 | 乘法 | 从左到右 | … * … |
除法 | … / … | ||
取模 | … % … | ||
13 | 加法 | 从左到右 | … + … |
减法 | … - … | ||
12 | 按位左移 | 从左到右 | … << … |
按位右移 | … >> … | ||
无符号右移 | … >>> … | ||
11 | 小于 | 从左到右 | … < … |
小于等于 | … <= … | ||
大于 | … > … | ||
大于等于 | … >= … | ||
in | … in … | ||
instanceof | … instanceof … | ||
10 | 等号 | 从左到右 | … == … |
非等号 | … != … | ||
全等号 | … === … | ||
非全等号 | … !== … | ||
9 | 按位与 | 从左到右 | … & … |
8 | 按位异或 | 从左到右 | … ^ … |
7 | 按位或 | 从左到右 | … | … |
6 | 逻辑与 | 从左到右 | … && … |
5 | 逻辑或 | 从左到右 | … || … |
4 | 条件运算符 | 从右到左 | … ? … : … |
3 | 赋值 | 从右到左 | … = … |
… += … | |||
… -= … | |||
… *= … | |||
… /= … | |||
… %= … | |||
… <<= … | |||
… >>= … | |||
… >>>= … | |||
… &= … | |||
… ^= … | |||
… |= … | |||
2 | yield | 从右到左 | yield … |
yield* | yield* … | ||
1 | 展开运算符 | n/a | ... … |
0 | 逗号 | 从左到右 | … , … |
参考: