首先,我可能应该问一下这是否与浏览器有关。
我读到过,如果发现无效的标记,但代码部分在该无效标记之前是有效的,则在换行符之前插入一个分号。
然而,通常引用分号插入引起的错误的常见示例是:
return
_a+b;
...这似乎不遵循此规则,因为 _a 将是一个有效的标记。
另一方面,分解调用链按预期工作:
$('#myButton')
.click(function(){alert("Hello!")});
有没有人能提供更详细的规则说明?
首先,我可能应该问一下这是否与浏览器有关。
我读到过,如果发现无效的标记,但代码部分在该无效标记之前是有效的,则在换行符之前插入一个分号。
然而,通常引用分号插入引起的错误的常见示例是:
return
_a+b;
...这似乎不遵循此规则,因为 _a 将是一个有效的标记。
另一方面,分解调用链按预期工作:
$('#myButton')
.click(function(){alert("Hello!")});
有没有人能提供更详细的规则说明?
首先你需要了解哪些语句会受到自动分号插入(也称为ASI)的影响:
var
语句do-while
语句continue
语句break
语句return
语句throw
语句ASI的具体规则在规范中有描述§11.9.1 自动分号插入规则
其中描述了三种情况:
LineTerminator
}
例如:
{ 1
2 } 3
被转化为
{ 1
;2 ;} 3;
NumericLiteral
1
符合第一个条件,它后面的标记是行终止符。
2
符合第二个条件,它后面的标记是}
。
例如:
a = b
++c
被转换为:
a = b;
++c;
限制性产生式:
UpdateExpression :
LeftHandSideExpression [no LineTerminator here] ++
LeftHandSideExpression [no LineTerminator here] --
ContinueStatement :
continue ;
continue [no LineTerminator here] LabelIdentifier ;
BreakStatement :
break ;
break [no LineTerminator here] LabelIdentifier ;
ReturnStatement :
return ;
return [no LineTerminator here] Expression ;
ThrowStatement :
throw [no LineTerminator here] Expression ;
ArrowFunction :
ArrowParameters [no LineTerminator here] => ConciseBody
YieldExpression :
yield [no LineTerminator here] * AssignmentExpression
yield [no LineTerminator here] AssignmentExpression
经典示例,使用 ReturnStatement
:
return
"something";
被转换为
return;
"something";
++c
后插入的部分吗? - BergiJavaScript 并不会把每个换行符作为分号,它只有在无法解析代码时才会把换行符视为分号。另外一段引文:针对下面的代码
var a
a
=
3 console.log(a)
JavaScript不会将第二个换行符视为分号,因为它可以继续解析更长的语句a = 3;
和:
有两个例外:当JavaScript无法解析第二行作为第一行语句的连续时,它会将换行符解释为分号。第一个例外涉及return、break和continue语句。
如果这些单词之后出现了换行符,JavaScript将始终将该换行符解释为分号。
第二种情况涉及++和--运算符。如果要使用这些运算符作为后缀运算符,则它们必须出现在应用于的表达式的同一行上。否则,换行符将被视为分号,并且++或--将被解析为前缀运算符,应用于其后面的代码。例如,请考虑以下代码:
x
++
y
这句话的意思是 x; ++y;
,而不是 x++; y
简单地说,一般情况下只要逻辑通顺,JavaScript 就会将代码看作是连续的,除了两种情况:(1) 在某些关键词后面,比如 return
、break
、continue
等等;(2) 如果出现了新的一行,并且这一行以 ++
或 --
开始,那么 JavaScript 会在前一行末尾添加一个分号 ;
。
“只要逻辑通顺”的部分让人感觉像正则表达式里的贪婪匹配。
因此,对于有换行符的 return
,JavaScript 解释器会插入一个分号:
(再次引用:如果这些关键词 [比如 return
] 后面跟着一个换行符…… 那么 JavaScript 总是把这个换行符解释成分号)
正是因为这个原因,经典的例子——
return
{
foo: 1
}
不会像预期的那样工作,因为JavaScript解释器会将其视为:
return; // returning nothing
{
foo: 1
}
在return
之后不能立即换行:
return {
foo: 1
}
为了使其正常工作。如果您遵循在任何语句后使用分号的规则,您可以自己插入一个 ;
:
return {
foo: 1
};
自动分号插入有三个基本规则:
LineTerminator
所分隔。Program
时,则在输入流的结尾自动插入分号。LineTerminator
here]”的受限制产生式内紧接着终结符或非终结符的第一个记号(因此这样的记号称为受限制的记号),并且受限制的记号与前面的记号至少被一个LineTerminator所分隔,则在受限制的记号前自动插入分号。但是,对于上述规则还有一个额外的覆盖条件:如果自动插入分号后分号会被解析为空语句或者该分号将成为for语句头部的两个分号之一(参见12.6.3),则不会自动插入分号。
关于自动分号插入和var语句,请注意在使用var跨越多行时不要忘记逗号。昨天,有人在我的代码中发现了这个问题:
var srcRecords = src.records
srcIds = [];
代码运行了,但效果是srcIds声明和赋值变成了全局变量,因为前一行带有var的本地声明不再适用,所以该语句被认为已经完成,出现了自动分号插入。
var srcRecords = src.records srcIds = [];
在一行中忘记了逗号或者你写成"return a && b"并没有遗漏任何东西...但是在a之前换行会在return后插入一个自动分号,这是由ASI规则定义的... - Sebastianvar
(let
, const
) 的清晰度胜过于打出这些字需要消耗的不到一秒钟的时间。 - squidbe补充一点,
const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
console.log("aa");
})()
++
和后缀--
之前(因此,在换行符之后的一元递增/递减运算符将作为前缀运算符绑定到以下(而不是上一个)语句)
- 在continue
、break
、throw
、return
、yield
之后
- 在箭头函数参数列表之后
- 在异步函数声明和表达式、生成器函数声明和表达式及方法以及异步箭头函数中async
关键字之后
开括号字符有多重含义。它可以表示一个表达式,也可以表示一个调用(与闭括号配对时)。
例如,以下代码会抛出 "Uncaught TypeError: console.log(...) is not a function" 错误,因为运行时尝试调用 console.log('bar')
的返回值:
let a = 'foo'
console.log('bar')
(a = 'bam')
let a = 'foo'
console.log('bar')
;(a = 'bam') // note semicolon at start of line
开头的方括号字符 ([
) 有多种含义。它可以表示对象属性访问,也可以表示数组的字面声明(当与右括号配对时),或者表示数组析取。
例如,以下代码会抛出 "Uncaught TypeError: Cannot set properties of undefined (setting 'foo')" 的错误,因为运行时尝试在 console.log('bar')
的响应上设置名为 'foo' 的属性值:
let a = 'foo'
console.log('bar')
[a] = ['bam']
let a = 'foo'
console.log('bar')
;[a] = ['bam'] // note semicolon at start of line