不知道年纪轻轻的你有没有过这种烦恼?
- 遇到过的问题,换个场面(面试or同事问到)还是不会?
- 一眼望去,简单简单,上手去做,直呼好难! 如果有,那么恭喜你小伙子,喜提“太自信”荣誉称号! 为了避免这些问题,我们就要放下身段,接受自己的帅气(美丽),好好记录、反思遇到过的问题才是发展之本
好了,扯了这么多,让我们接下来一起看看这道题你是否熟悉,或者说这次遇到的时候是不是还是知道答案,并且说出为什么呢?
抛转 - 问题
下面这两条语句,在浏览器的控制台里会打印出什么呢?
({}+10)
{}+10
这里就不和大家卖关子了,打印结果如下:
不知道上面的打印结果是否符合你的预期呢?
如果是,并且知道是为什么,那就不用在弟弟这篇文章中浪费时间啦,顺手点个赞就可以去get其他知识点了~
如果不是,那么我们就继续向下说道说道,首先我们先来把心里的疑惑屡清楚
梳理疑点
-
首先第一个加圆括号的语句打印出来的结果是符合我们的认知的
+
运算符是有一个隐式转换的规则的,转换规则很复杂,针对我们这道题只需要知道,+
运算符遇到一个对象和数字相加时会首先将对象调用toString
方法进行转换,然后将数字转换成字符串,最后将两个字符串进行拼接。
上面我们提到的转换,用行话说叫“拆箱”,针对Object类型的拆箱操作我们需要知道这些知识点
Object
进行类型转换的时候,会优先调用[symbol.toPrimitive]
()- 当
Object
遇到+
号并且没有toPrimitive
的时候会优先调用valueOf
方法 (前提:需要转换成Number时就调用valueOf
) - 当
Object
作为key
属性存在时会优先调用toString
方法(前提:需要转换成string时就调用toString
)- eg:
Obj[key]
这里就会调用toString
方法
- eg:
所以针对这个表达式,我们可以理解为以下操作:
const obj = {} const str = obj.toString() console.log( str + 10 ) // "[object Object]10"
-
其次就是为什么第二个只是少了个圆括号,就会造成这样的差异呢?这应该是我们最困惑的点,其实这也是今天我想说的最重要的问题。
对于这个问题,其实我想只要我换一种写法大家就会知道了:
看到了吗?加了分号之后是不是就比较符合我们的认知了,瞬间感觉第二个语句打印的结果是这么的顺其自然。
所以这个分号是怎么回事呢?为什么我们第一次打印的明明没有加分号,打印出来的却和加了分号的结果一样呢?
其实这里是因为js在解释执行的时候
{}
会被解析成BlockStatement
(复合语句),相当于是一个整体,只不过这个块中没有任何内容而已,所以不会进行转换,只会对+10
进行输出一切都清晰了,但是文章还没有因此结束,因为我们上面提到了加分号,虽然对于这个语句其实在js执行的时候是没有在gif图中的位置加分号的,我这里是为了让大家清晰的分辨前面是一个复合语句而做的处理。
虽然它没加但是不证明它没有加分号的这个机制,其实在js里还真的有自动添加分号这么一个东西,并且影响这我们的代码
那么就到了今天的“引玉”环节了。
引玉 - ASI
首先,我带大家先普及一下基本知识:
何为 ASI?
ASI(Automatic semicolon insertion: 中文全称“自动分号补全”。
ECMAScript标准中规定,一些特定的JavaScript语句必须要以分号结尾,用分号来标识这个语句的终点。但是我们有很多时候为了让代码简洁都是会省略分号的,这种行为在JavaScript中也是被允许的,因为在代码编译的时候解释器会帮助我们自动添加分号,那么对于解释器来说就有一套自己的规则去界定何时添加分号,我们这里简单总结一下:
特定的语句
所有特定语句可以看MDN官网:点我
- 空语句
- let、const、变量声明
- import、export、模块定义
- 表达式语句
- debugger
- continue、break、throw
- return
我们的代码在解析的时候会被从左到右扫描,并被转换为一系列的输入元素,包括 token、控制符、行终止符、注释和空白符。
也就是说对于上面的这些特定语句,解释器解析到的时候ASI机制就会主动识别并且添加上分号,下面我们具体来说一下这个过程:
- 遇到一个不被允许的行终止符符或者
}
时
行终止符(line terminators):简单说来就是一些换行(\n)、回车之类(\r)的。大家可以从官网看的更详细的信息,这里就不赘述了
{ 1 2 } 3
// ASI转换: 解析时遇到 } 在前面加上分号,继续解析发现3后面是个换行符再加上分号,就会变成下面这样子
{ 1 2 ;} 3;
- 当捕获到标识符输入流的结尾,并且无法将单个输入流转换为一个完整的程序时,将在结尾插入一个分号。 对于这点我的理解是,对于一个语句进行解析的时候,会对每个token进行解析,直到遇到类似终止符的情况结束。如果每个解析出来的token无法单独完成一个有意义的执行程序,那么这些token将会被组合在一起当做一个完整的执行语句(这个过程中解释器会将尽量多的token去当成一个完整的语句)。最后就会在末尾添加分号作为标识
a = b
++c
解析器进行词法(token)分析的时候会进行类似这样的操作:
a
=
b
换行符
+
+
c
换行符
根据这个规则就会转换成下面的格式
a = b;
++c;
- 当语句中包含一个[受限的语法并且后面跟着一个行终止符]的时候,将会在结尾插入一个分号。
受限的语法包括:
- 后置运算符(++ 和 --)
- continue
- break
- return
- yield、yield*
- module
return
a + b
// return 在受限语法之列,并且后面有换行符所以会直接在后面添加分号
return;
a + b;
所以这里我们需要注意: 在代码中 return 之后不要回车然后再带上需要返回的内容,因为这时return的就是undefined了
ECMA中对ASI的解释
链接在这里:ES2015
上面我们讲到的规则在这些定义里面都有体现,大家感兴趣的可以看一下官方的原文。
好了,今天就聊到这里了~因为这也是今天学习整理的,可能会有错误的地方,希望大家可以指出,也希望大家路过给码了2000字的弟弟点个赞