运算符优先级和短路运算

一、短路运算

逻辑与&&的第一个操作数的值是false时,直接返回第一个操作数的值,不再对第二个操作数进行计算

逻辑或||的第一个操作数的值是true时,直接返回第一个操作数的值,不再对第二个操作数进行计算


二、运算符优先级

优先级表格


三、求值顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function fn01() {
console.log('fn01');
return false;
}

function fn02() {
console.log('fn02');
return true;
}

function fn03() {
console.log('fn03');
return true;
}

console.log(fn01() || fn02() && fn03()); // fn01 => fn02 => fn03 => true

要注意区分优先级结合性求值顺序三者的区别。

这三个是不同的概念,却经常被混淆。通过AST来看就很容易理解:(假设源码是从左到右输入的)

所谓优先级,就是不同操作相邻出现时,AST节点与根的距离的关系。优先级高的操作会更远离根,优先级低的操作会更接近根。为什么?因为整棵AST是以后序遍历求值的,显然节点离根越远就越早被求值。

所谓结合性,就是当同类操作相邻出现时,操作的先后顺序同AST节点与根的距离的关系。如果是左结合,则先出现的操作对应的AST节点比后出现的操作的节点离根更远;换句话说,先出现的节点会是后出现节点的子节点。

所谓求值顺序,就是在遍历子节点时的顺序。对二元运算对应的节点来说,先遍历左子节点再遍历右子节点就是从左到右的求值顺序,反之则是从右到左的求值顺序。

这三个概念与运算的联系都很紧密,但实际描述的是不同的关系。前两者是解析器根据语法生成AST时就已经决定好的,后者则是解释执行或者生成代码而去遍历AST时决定的。

在没有副作用的环境中,给定优先级与结合性,则无论求值顺序是怎样的都能得到同样的结果;而在有副作用的环境中,求值顺序会影响结果。


对应console.log(fn01() || fn02() && fn03()),AST是:

1
2
3
4
5
     ||
/ \
fn01() &&
/ \
fn02() fn03()

表达式层面上,代码的执行就是后序遍历这棵AST而已。

优先级:在相邻的两个运算符 || 与 && 中,&& 比 || 离AST的根更远所以优先级更高

结合性:这里没有相邻的同优先级运算符所以展现不出 || 与 && 的结合性,虽然它们都是左结合的

求值顺序:JavaScript的求值顺序就是从左向右的。对这棵AST来说就是在后序遍历时,每个节点的求值过程是先遍历左子树,再遍历右子树,然后对自己求值。


AST生成传送门