Although JavaScript is very powerful, the language’s fundamentals do not have a very steep learning curve.  Prior to the explosion of web applications, JavaScript was thought of as a toy language for amateur programmers.  Some of JavaScript’s features were specifically designed to cater to beginners.  One such feature is automatic semicolon insertion.  Automatic semicolon insertion is also one of JavaScript’s most controversial features.

JavaScript’s syntax borrows heavily from C and Java.  Programmers that are familiar with these languages are accustomed to semicolon terminated statements.  JavaScript statements are also terminated by semicolons, but unlike C and Java, these semicolons are not always required.  In an effort to “help” programmers, the JavaScript interpreter will actually insert omitted semicolons where it deems necessary.  Unfortunately, automatic semicolon insertion, which was intended to act as a crutch for programmers, can actually introduce difficult to find bugs.  In my opinion, it also promotes bad programming practices by not forcing developers to properly terminate statements.

Section 7.9.1 of the ECMAScript 5.1 Standard specifies rules governing automatic semicolon insertion.  To understand the rules, you should first understand the concept of tokens.  Most of the rules involve inserting a semicolon at the end of a line of code ― referred to as a LineTerminator token.  Because automatic semicolon insertion rules are related to line breaks, whitespace can effect the execution of JavaScript programs.  Many common languages (C, Java, HTML) allow developers to ignore whitespace.  Developers who are familiar with these languages can run into problems in JavaScript due to this assumption.

It is important to recognize the scenarios where automatic semicolon insertion is applied.  There are also a number of scenarios where semicolons are not automatically inserted.  The following sections describe the rules for inserting (or not inserting) semicolons automatically.

LineTerminator, Closing Braces, and End of Stream

The JavaScript interpreter will insert a semicolon between two statements when they are separated by a LineTerminator or a } token.  A semicolon will also be inserted, if needed, at the end of the input stream.  The following ‘if’ statement is, surprisingly, valid JavaScript.

if (i === 0) {
foo = 1
bar = 2 } baz = 3

A semicolon is inserted between the ‘foo’ and ‘bar’ assignment statements because they are separated by a LineTerminator.  Another semicolon is inserted after the ‘bar’ assignment because the next token is a closing curly brace.  A final semicolon is inserted after the ‘baz’ assignment because the end of the input stream has been reached.  After semicolon insertion, the ‘if’ statement looks like this:

return, throw, continue, and break Statements

If a LineTerminator token is encountered immediately after a ‘return’, ‘throw’, ‘continue’, or ‘break’ token, a semicolon is automatically inserted.  This means that labels in ‘continue’ and ‘break’ statements must be specified on the same line as the respective ‘continue’ or ‘break’ token.  Similarly, expressions in ‘return’ and ‘throw’ statements must begin on the same line as the ‘return’ or ‘throw’ token.  For example, the following ‘return’ statement does not exhibit the behavior that the developer likely intended.

return
a + b;

The developer most likely intended to return the result of the expression ‘a + b’.  However, when this ‘return’ statement is parsed by the interpreter, it is transformed to look like the following code.  In this case, the return value is undefined and the ‘a + b’ expression becomes unreachable code.

return;
a + b;

‘return’ statements that return object literals are potentially the most common victims of semicolon insertion related bugs.  Object literal syntax lends itself well to being split across multiple lines.  This is especially true for large objects.  For example, the following function returns an undefined value.

function getObject() {
return
{
foo : 1
// many more fields
};
}

Postfix Operators

The postfix operators ‘++’ and ‘–’ must appear on the same line as their operand.  If a LineTerminator occurs between the operand and the operator, then a semicolon will be inserted by the interpreter.  These mistakes are uncommon.  For example, a developer is unlikely to write ‘i++’ on multiple lines.

for Statements

The header of a ‘for’ loop must always contain two semicolons.  According to the specification, semicolons are never automatically inserted into the header of a ‘for’ loop.  This means that the programmer is responsible for including both semicolons.  For example, the following loops are valid JavaScript:

for (var i = 0; i < 5; i++) {
// loop body
} for (; ;) {
// loop body
} for (var i = 0; i < 5;
i++) {
// loop body
}

However, the following loops are not valid because the missing second semicolon is not automatically inserted.

for (var i = 0; i < 5
i++) {
// loop body
} for ( ;
) {
// loop body
}

Empty Statements

Semicolons are also never inserted when the resulting statement would be the empty statement ― a statement that consists of only a semicolon.  The following ‘if-else’ statement is invalid.  The interpreter will not insert a semicolon in the ‘if’ clause because the resulting statement would be empty.

if (i === 5)
// no semicolon will be inserted here
else
foo = 0;

A More Complicated Example

All semicolons have been removed from the following example.  In this case, it can be more difficult to determine the semantics of the code.  What will the value of ‘foo’ be at the end of the example?

var foo
var bar
var baz = function(data) {
return data +
1
} bar = 1
foo = bar + baz
(bar + bar) + baz(bar)

Let’s analyze the code.  The first three lines declare the variables ‘foo’, ‘bar’, and ‘baz’.  ’baz’ is a function that increments its ‘data’ argument by one.  Because the expression ‘data + 1′ begins on the same line as the ‘return’ token, ‘baz’ returns the expected value.  The ‘bar’ assignment statement is straightforward.  The ‘foo’ assignment is trickier.  A semicolon is not inserted between the last two lines because the opening parentheses indicates a function call that began on the previous line.  Therefore, the ‘foo’ assignment actually looks like this:

 foo = bar + baz(bar + bar) + baz(bar);

Now it is fairly simple to compute ‘foo’.  The final value of ‘foo’ is six.

Things to Remember

  • If the programmer leaves out a semicolon, the JavaScript interpreter will insert it automatically in some circumstances.

  • Automatic semicolon insertion can introduce bugs which are difficult to locate because whitespace changes semantics.

  • Programmers should never rely on automatic semicolon insertion.

Appendix

The following excerpt is taken directly from Section 7.9.1 of the standard, which describes the rules for automatic semicolon insertion.  The ‘restricted productions’ mentioned in Rule #3 relate to postfix operators and the ‘continue’, ‘break’, ‘return’, and ‘throw’ statements.

Begin Excerpt

There are three basic rules of semicolon insertion:

  1. When, as the program is parsed from left to right, a token (called the offending token) is encountered that is not allowed by any production of the grammar, then a semicolon is automatically inserted before the offending token if one or more of the following conditions is true:

    • The offending token is separated from the previous token by at least one LineTerminator.

    • The offending token is }.

  2. When, as the program is parsed from left to right, the end of the input stream of tokens is encountered and the parser is unable to parse the input token stream as a single complete ECMAScript Program, then a semicolon is automatically inserted at the end of the input stream.

  3. When, as the program is parsed from left to right, a token is encountered that is allowed by some production of the grammar, but the production is a restricted production and the token would be the first token for a terminal or nonterminal immediately following the annotation ―[no LineTerminator here]‖ within the restricted production (and therefore such a token is called a restricted token), and the restricted token is separated from the previous token by at least one LineTerminator, then a semicolon is automatically inserted before the restricted token.

However, there is an additional overriding condition on the preceding rules: a semicolon is never inserted automatically if the semicolon would then be parsed as an empty statement or if that semicolon would become one of the two semicolons in the header of a for statement (see 12.6.3).

End Excerpt

 

所以在javascript中很多项目经理都要求

function test(){
console.log('test');
}

而不喜欢这种格式:

function test()
{
console.log('test');
}

就是希望最大限度减少这种问题的出现

The Dangers of JavaScript’s Automatic Semicolon Insertion的更多相关文章

  1. JavaScript & Automatic Semicolon Insertion

    JavaScript & Automatic Semicolon Insertion ECMA 262 真香警告️ https://www.ecma-international.org/ecm ...

  2. JavaScript Semicolon Insertion

    JavaScript Semicolon Insertion https://blog.izs.me/2010/12/an-open-letter-to-javascript-leaders-rega ...

  3. auto semicolon insertion 自动分号补齐的坑

    今天发现js自动分号补齐的坑,来看如下两段代码: function Hello(){ return { name: ’JavaScript’ }; } alert(Hello()); //输出unde ...

  4. 读《编写可维护的JavaScript》第一章总结

    第一章 基本的格式化 1.4 ① 换行 当一行长度到达了单行最大的字符限制时,就需要手动将一行拆成俩行.通常我们会在运算符后换行,下一行会增加俩个层级的缩进. // 好的做法: 在运算符后换行,第二行 ...

  5. JavaScript简易教程(转)

    原文:http://www.cnblogs.com/yanhaijing/p/3685304.html 这是我所知道的最完整最简洁的JavaScript基础教程. 这篇文章带你尽快走进JavaScri ...

  6. JavaScript 常见错误

    1. 严格缩进 JavaScript 会自动添加句末的分号,导致一些难以察觉的错误 return { key: value }; // 相当于 return; { key: value }; 2. 括 ...

  7. 良好的JavaScript编码风格(语法规则)

    编码风格 1.概述 "编程风格"(programming style)指的是编写代码的样式规则.不同的程序员,往往有不同的编程风格. 有人说,编译器的规范叫做"语法规则& ...

  8. Expressions versus statements in JavaScript

    Statements and expressions An expression produces a value and can be written wherever a value is exp ...

  9. 温故而知新--JavaScript书摘(二)

    前言 毕业到入职腾讯已经差不多一年的时光了,接触了很多项目,也积累了很多实践经验,在处理问题的方式方法上有很大的提升.随着时间的增加,愈加发现基础知识的重要性,很多开发过程中遇到的问题都是由最基础的知 ...

随机推荐

  1. Amazon Alexa 语音识别2 : 设置

    开发者建立的Skill的主要设置项目都在Skill的console内.需要填写的东西大致有以下几个: 1.Skill 名字.这个名字是用户用来唤醒你这个Skill的. 2.Intent Schema: ...

  2. objective_C 优缺点

    objective-c语言的优缺点 objc优点: 1) Cateogies 2) Posing3) 动态识别4) 指标计算5)弹性讯息传递6) 不是一个过度复杂的 C 衍生语言7) Objectiv ...

  3. iOS 日历类(NSCalendar)

    对于时间的操作在开发中很常见,但有时候我们需要获取到一年后的时间,或者一周后的时间.靠通过秒数计算是不行的.那就牵扯到另外一个日历类(NSCalendar).下面先简单看一下 NSDate let d ...

  4. sql关键查询

    情境:保留表A数据,且A表与B表是一对多关系 SELECT tuf.Id,tuf.FileName,tuf.type,tuf.url,tum.MachineId,tum.IsDownland,tum. ...

  5. pyshp操作shapefile

    ESRI的shp文件自1998发布技术文档以来,shp作为GIS文件的基本交换文件广为使用. 工作中使用shp文件的机会比较多,pyshp是Python操作shapefile的包. 先来说shp文件的 ...

  6. 1010 [HNOI2008]玩具装箱toy

    斜率优化dp: 推荐学习http://www.cnblogs.com/perseawe/archive/2012/05/12/bz1010.html 看着别人的题解自己学着推,终于理解了 #inclu ...

  7. Spring 配置XML文件头部文件格式

    普通格式: <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns:xsi="ht ...

  8. push 栈顶sp=sp-2 可以把立着的栈,向左侧倒下,那么形态就和反汇编时,内存的形态是一样的。小偏移的字节在前, 大的偏移字节在后

    push  栈顶sp=sp-2 可以把立着的栈,向左侧倒下,那么形态就和反汇编时,内存的形态是一样的.小偏移的字节在前, 大的偏移字节在后. 1 2 3 4 5 1 2 3 4 5

  9. 《ArcGIS Engine+C#实例开发教程》第二讲 菜单的添加及其实现

    原文:<ArcGIS Engine+C#实例开发教程>第二讲 菜单的添加及其实现 摘要:在上一讲中,我们实现了应用程序基本框架,其中有个小错误,在此先跟大家说明下.在“属性”选项卡中,我们 ...

  10. velocity-1.7中vm文件的存放位置

    velocity-1.7中关于vm文件存放 demo: public class App_example1 { public App_example1() { String propfile=&quo ...