几个关于运算符优先级的陷阱

前言
JS中的表达式非常强大,对应的是与表达式相关的各种运算符,这些运算符能够任意组合,赋予JS极大的灵活性和强大的组合性,很多时候用运算符就可代替许多用数行语句才能实现的功能,在这运算符强大的背后,隐藏着优先级带来的隐患
大多JS开发者对于运算符的优先级并不非常熟悉,这导致了如果肆意使用运算符,就可能因为优先级而导致诸多问题,甚至造成实际生产事故,所以,在拥抱运算符带来的强大力量的同时,运算符带来的潜在的危险性也不容忽视
例子
一个例子 我们可能之前有以下老代码

// Order 是一个构造函数,用于创建订单 function Order { // 一些代码 } // 如果 order 是 Order 的实例,做一些事,否则,做一些事 if(order instanceof Order) { // 一些代码 }

业务变了,现在需要在 order 不是 Order 的实例的情况下做一些事
对于新手可能会这么写
if(order instanceof Order) { }else { // 一些代码 }

这运行起来是正确的,但是条件为true的块就空了,很不优雅
因此熟悉运算符的人有可能会这么写
if(!order instanceof Order) { // 一些代码 }

陷阱 对于这段代码的执行
参考下面的测试用例
let order = new Order(); // 我们的代码 !order instanceof Order // false // 我们期望的执行时的优先级 !(order instanceof Order) // false

目前,看起来是正确的
但是,如果是下面这个用例
let order = {}; // 我们的代码 !order instanceof Order // false // 我们期望的执行时的优先级 !(order instanceof Order) // true

这产生了不同的结果,尚未使用圆括号包裹不同的运算符,这可能导致问题,因为 ! 的优先级高于 instanceof
所以上述表达式实际上是这样运行的
(!order) instanceof Order;

这个表达式始终返回 false
而我们期望在 order 不为 Order 实例的时候返回 true, 否则 false
所以,对于上述情况,必须要手动添加括号确保优先级
!(order instanceof Order);

上述情况是个常见的例子,我们经常会使用 !xxx, !xx() !xx.xx[xx]() ,因此就可能出现上述问题
两个其他的例子 运算符 ! typeof 和 === 参考以下代码
// 表达式1 typeof a === "string" // 和 表达式2 !typeof a === "string"

常规用法,我们知道, typeof 的优先级大于 === 的优先级所以我们知道第一个表达式是这样执行的
(typeof a) === "string"

实际上 ! 的优先级和 typeof 的优先级相同, 但是大于 === 的优先级
所以上述表达式2实际是这样的
(!(typeof a)) === "string"

【几个关于运算符优先级的陷阱】而我们期望的执行是这样的
!((typeof a) === "string")

对于上述情况,建议改用 !== 运算符,这样就不必在前面使用 ! ,也不必考虑优先级问题,如果确实出现了类似情况需要前面加 ! ,确保加上圆括号
逻辑运算符 && || ?? 对于这三者,如果不熟悉的话可能会认为优先级相同,这可能会导致如下代码
a && b && c || d

因为 && 优先级高于 ||, 所以上述代码实际执行的优先级是,
(a && b && c) || d

这有可能不符合预期,有些时候我们的预期是这样的
a && b && (c || d)

对于这种情况,必须加上圆括号以确保优先级
对于 ?? 运算符,我们可以看作和 || 是相同类型,都是在左侧不符合条件的时候使用右侧
实际上这三者的优先级是 && 高于 || 和 ??, || 和 ?? 是相同的优先级
参考
mdn web docs 运算符优先级
结语
我建议大多数时候在不同的运算符结合使用时添加圆括号
如果一眼不能看出优先级高低,最好添加圆括号确定优先级

    推荐阅读