# 表达式&&还会短路?

你是否看一眼就知道表达式console.log(1&&2)的输出结果?如果是,以下内容可以不用看了。本文只是围绕这一问题由点及面展开了一些基础知识点的整理,方便后续查阅。

# 首先抛出一些基础题

console.log(1 || 2) // 1
console.log(0 || 1); // 1
console.log(0 && 1); // 0
console.log(1 && 0); // 0
console.log(1&&2); // 2
console.log('1' + 2 + 3); // 123
console.log(3 + 4 * 5); // 3 + 20
console.log(3 > 2 > 1); // false( 分解:(3>2)>1 => true>1 => false)
console.log(3 > 2 && 2 > 1); // true
console.log([] == false) // true
console.log(![]) // false
console.log(!![]) // true
console.log(undefined == null) // true
console.log(undefined === null) // false
console.log(null == null) // true
console.log(null === null) // true
console.log(typeof null) // "object" // 因null的二进制前三位都为0
console.log(NaN == NaN) // false
console.log("3" > "23") // true // 因"2"的字符编码是50,"3"的字符编码是51
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

由以上可知,可总结出以下结果:

  • 逻辑运算符“||”和“&&”都是遵行短路原则,只要确定符号前面的真假,既可确定返回值,故表达式console.log(1&&2)的值为2。
  • 对于typeof null为object的理解,因为不同的对象在底层都表示为二进制,在JS中二进制前三位都为 0 的话会被判断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“ object ”。
  • 左右两个操作数都是字符串进行大小比较时,此时比较的是字符编码;

# JS表达式和运算符分类

运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。以下将详细介绍表达式的分类和优先级:

# 主要表达式

JavaScript中基本关键字和常用表达式:

  • this,this关键字指向函数的执行上下文。
  • function,function关键字定义了函数表达式。
  • class 关键字定义了类表达式。
  • function*,function* 关键字定义了一个 generator 函数表达式。
  • yield,暂停和恢复generator函数。
  • yield*,委派给另外一个generator函数或可迭代的对象。
  • async function,async function 定义一个异步函数表达式。
  • await,暂停或恢复执行异步函数,并等待promise的resolve/reject回调。
  • [],数组初始化/字面量语法。
  • {},对象初始化/字面量语法。
  • /ab+c/i,正则表达式字面量语法。
  • (),分组操作符。

# 左表达式

左边的值是赋值的目标。

  • 「属性访问符」成员运算符提供了对对象的属性或方法的访问(object.propertyobject["property"])。
  • new运算符创建了构造函数实例。
  • 「new.target」在构造器中,new.target指向new调用的构造器。
  • super关键字调用父类的构造器。
  • 「...obj」展开运算符可以将一个可迭代的对象在函数调用的位置展开成为多个参数,或者在数组字面量中展开成多个数组元素。

# 自增和自减

前置/后置自增运算符和前置/后置自减运算符(A++, A--, ++A, --A)。

# 一元运算符

一元运算符只有一个操作数。

  • delete 运算符用来删除对象的属性。
  • void 运算符表示表达式放弃返回值。
  • typeof 运算符用来判断给定对象的类型。
  • 「+」一元加运算符将操作转换为Number类型。
  • 「-」一元减运算符将操作转换为Number类型并取反。
  • 「~」按位非运算符。
  • 「!」逻辑非运算符。

# 算术运算符

算术运算符以二个数值(字面量或变量)作为操作数,并返回单个数值。有加(+)、减(-)、乘(*)、除(/) 和 取模(%)这些运算符。

# 关系运算符

比较运算符比较二个操作数并返回基于比较结果的Boolean值。

  • in运算符用来判断对象是否拥有给定属性。
  • instanceof 运算符判断一个对象是否是另一个对象的实例。
  • 大于(>)、大于等于(>=)、小于(<)、小于等于(<=)。

# 相等运算符

如果相等,操作符返回的是Boolean(布尔)类型的true,否则是false。有相等(==)、不等(!=)、全等(===)、非全等(!==)这些运算符。

# 位移运算符

在二进制的基础上对数字进行移动操作。有按位左移(<<)、按位右移(>>)、按位无符号右移(>>>)这些运算符。

# 二进制位运算符

二进制运算符将它们的操作数作为32个二进制位(0或1)的集合,并返回标准的JavaScript数值。有二进制位与(&)、二进制位或(|)、二进制位异或(^)这些运算符。

# 二元逻辑运算符

逻辑运算符典型的用法是用于boolean(逻辑)值运算, 它们返回boolean值。有逻辑与(&&)、逻辑或(||)这些运算符。它们都遵行短路原则,只要确定符号前面的真假,既可确定返回值,总结如下:

  • 只要“||”前面为false,无论“||”后面是true还是false,结果都返回【“||”后面】的值。
  • 只要“||”前面为true,无论“||”后面是true还是false,结果都返回【“||”前面】的值。
  • 只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返【“&&”前面】的值。
  • 只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返【“&&”后面】的值。

# 条件(三元)运算符

条件元素运算符把两个结果中其中一个符合运算逻辑的值返回(condition ? ifTrue : ifFalse)

# 赋值运算符

赋值元素符会将右边的操作数的值分配给左边的操作数,并将其值修改为右边操作数相等的值。有赋值运算符(=)、赋值求和(+=)、左位移(<<=)等运算符(查看更多 (opens new window)),赋值符号经典用法:

  • 数组交换值:[a, b] = [1, 2]
  • 结构赋值:const {name, age} = { name: 'james9527', age: 18 }

# 逗号操作符

「,」逗号操作符允许在一个判断状态中有多个表达式去进行运算并且最后返回最后一个表达式的值。

# 运算符优先级汇总表

下面的表将所有运算符按照优先级的不同从高(20)到低(0)排列,大部分运算符都是从左到右的顺序执行,部分从右到左,如赋值=、+=等运算符。

优先级 运算类型 关联性 运算符
20 圆括号 n/a(不相关) ( … )
19 成员访问 从左到右 … . …
19 需计算的成员访问 从左到右 … [ … ]
19 new (带参数列表) n/a new … ( … )
19 函数调用 从左到右 … ( … )
18 new (无参数列表) 从右到左 new …
17 后置递增(运算符在后) n/a … ++
17 后置递减(运算符在后) n/a … --
16 前置递增/递减 从右到左 ++/-- …
16 逻辑非 从右到左 ! …
16 按位非 从右到左 ~ …
16 一元加法/减法 从右到左 +/- …
16 typeof/void/delete/await 从右到左 typeof/void/delete/await …
15 从右到左 … ** …
14 乘法 从左到右 … * …
14 除法 从左到右 … / …
14 取模 从左到右 … % …
13 加法 从左到右 … + …
13 减法 从左到右 … - …
12 按位左移 从左到右 … << …
12 按位右移 从左到右 … >> …
11 小于(等于) 从左到右 … <(<=) …
11 大于(等于) 从左到右 … >(>=) …
11 in 从左到右 … in …
11 instanceof 从左到右 … instanceof …
10 等号 从左到右 … == …
10 非等号 从左到右 … !== …
10 全等号 从左到右 … === …
10 非全等号 从左到右 … !== …
9 按位与 从左到右 … & …
8 按位异或 从左到右 … ^ …
7 按位或 从左到右 … 单竖线 …
6 逻辑与 从左到右 … && …
5 逻辑或 从左到右 … 双竖线 …
4 条件运算符 从右到左 … ? … : …
3 赋值 从右到左 … =(+=/-=/%=/&=/<<=/...) …
2 yield(*) 从右到左 yield …
1 展开运算符 n/a 三个点 …
0 逗号 从左到右 … , …

# 参考资料

运算符优先级(MDN) (opens new window)