JS代码需不需要加分号,有这个问题就证明JS加不加分号都行,加了会减轻编译器的工作负担,不加编译器会自动补全分号,减轻开发者的负担。
编译器在处理JS代码的时候,找到分号就直接当语句结束,没找到会按照特定规则对分号进行补全。本文主要内容为:
分号补全规则具体细节
不加分号可能会遇到的问题以及解决方案
分号自动补全规则
主要规则如下:
如果遇到换行符,且下一个符号是不符合语法的,会尝试插入分号;
如果遇到换行符,且规定此处不能有换行符,会自动插入分号;
源代码结束的时候,不能形成完整的脚本或者模块结构,会自动插入分号。
下面举几个例子🌰;
对应规则1
const foo = 1
void function (foo ){
console .log (foo);
}(foo);
第一句let foo = 1
后面有换行符,然后遇到void
关键字,不符合语法规则,所以会在void
前面加分号。
对应规则2
const foo = 1 ;
const bar = 1 ;
const baz = 1 ;
foo
++
bar
++
baz
foo++
是合法的,但是JS规定(这里的规定后面会细说),foo
和++
之间不能加换行符,所以会在foo
后面加入分号,最后代码相当会变成这样:
const foo = 1 ;
const bar = 1 ;
const baz = 1 ;
++bar;
++baz;
对于规则3,加不加分号都无所谓,反正是在代码最后面,不影响前面代码的执行,所以就不举例子了。
不能有换行符的语法
在JS标准中,有个叫 [no LineTerminator here] 的规则,规定哪些语法不能加入换行符,如果开发者加了换行符,则JS编译器会无法识别并加入分号。
带标签的continue
语句,不能continue
后插入换行;
带标签的break
语句,不能在break
后面插入换行;
return
后面不能插入换行;
后自增、后自减运算符前不能插入换行;
throw
和Exception
之间不能插入换行;
async
关键字,后面不能插入换行;
箭头函数的箭头前,不能插入换行;
yield
之后,不能插入换行。
下面用代码展示一下:
带标签的continue
语句,不能continue
后插入换行:
tag :for (var j = 0 ; j < 10 ; j++)
for (var i = 0 ; i < j; i++)
continue tag
带标签的break
语句,不能在break
后面插入换行:
tag :for (var j = 0 ; j < 10 ; j++)
for (var i = 0 ; i < j; i++)
break tag
return
后面不能插入换行:
function sum (a, b ) {
return a + b;
后自增、后自减运算符前不能插入换行:
i++
i--
throw
和Exception
之间不能插入换行:
throw new Exception ("error" )
async
关键字,后面不能插入换行:
async function foo (){
const foo = async x => x*x
箭头函数的箭头前,不能插入换行:
const foo = x => x*x
yield
之后,不能插入换行:
function *g (){
var i = 0 ;
while (true ) {
yield i++;
以上所有情况都会在JS执行时在/*no LineTerminator here*/
处加上分号。但是JS这些规则还不够完善,会有几种情况不符合预期,需要开发者而外注意。
不加分号需要注意的情况
下面列举几个不加分号需要注意的情况。
以括号开头的语句
(function (a ){
console .log (a);
})()
(function (b ){
console .log (b);
这里开发者的意图是想执行两个立即执行函数( IIFE ),但是()
后面还是()
,在JS看来是合法的语句(如:函数返回一个函数再调用),所以这里不会被自动加入分号,第二个 IIFE 会被当做第一个 IIFE 执行完之后继续执行的入参。然后由于第一个 IIFE 没有返回函数,这里会抛出一个xxx is not a function
的类型错误。
以数组开头的语句
const arr = [[1 , 2 ]]
[3 , 2 , 1 , 0 ].forEach (item => console .log (item))
这里开发者的意图是想给arr
遍历赋值,然后遍历一个数组打印每一项的值,但是由于不会自动加分号,这里会被解读成下表运算符和逗号运算符,并且不会报错!!!
代码解读:首先[3, 2, 1, 0]
会被当做[[1, 2]]
的下标,然后3, 2, 1, 0
执行逗号运算符的结果是0,最后代码等价于:
const arr = [[1 , 2 ]][0 ].forEach (item => console .log (item))
结果完全不符合预期,还不报错,调试起来会异常困难。
以正则表达式开头的语句
这个例子让我想到了XSS攻击 ,利用语法规则对代码解读进行干预,从而窃取用户信息。
const x = 1
/(a)/g.test ("abc" )
console .log (RegExp .$1 )
这里开发者的意图是给变量x
赋值,并打印正则匹配的结果。但是由于不会自动加分号,正则表达式的第一个/
会被当成除号,1
➗(a)/g.test("abc")
结果为NaN
,所以实际没有执行test
函数。
这里的函数也不会报错,找起问题来会很dan疼。
以 Template 开头的语句
这种情况比较少见,在和正则配合使用的时候会出现问题。
const f = function (){
return "" ;
const g = f
`Template` .match (/(a)/ );
console .log (RegExp .$1 )
这里开发者的意图是将函数f
赋值给函数g
,然后执行match
方法,查看正则匹配的值。但是由于不会自动加分号,f
会和Template
连起来解读,作为f
的参数传入。
注意,函数是可以跟着模板字符串表示执行函数,字符串内容会被保存到arguments
中。
var f = function (){
console .log (arguments );
f`Template`
f`Template${1 } `
如何解决上述问题
最保险的方法当然是自己加分号。
如果你实在不想加,可以利用一些工具自动补全分号,如配合eslint
和vs code
一起使用,在保存的时候根据规则自动补充分号:
eslintrc
中加入这条规则:"semi": ["error", "always"]
vs code
中设置一下:"eslint.autoFixOnSave": true
也可以直接在eslint
中限制写法,从源头解决问题。如:使用no-plusplus 限制自增符号的使用,用+=
的方式替代。
以上就是本文的全部内容,个人觉得其实加不加分号都行,主要团队内要统一,别一会儿加一会儿不加。而且需要注意的规则也不多,遇到过几次估计都记住了。最后希望大家看完之后有所收获,将bug扼杀在摇篮里。
ESLint
3.9w
xiaohesong
ECMAScript 6