-
Notifications
You must be signed in to change notification settings - Fork 1
/
javascript-semicolons.html
832 lines (493 loc) · 41.1 KB
/
javascript-semicolons.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
<!doctype html>
<html class="theme-next mist use-motion">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.4.0" rel="stylesheet" type="text/css" />
<link href="/css/main.css?v=5.0.1" rel="stylesheet" type="text/css" />
<meta name="keywords" content="js,semicolon,asi," />
<link rel="alternate" href="/atom.xml" title="清风轩" type="application/atom+xml" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.0.1" />
<meta name="description" content="这是一篇发表于2010年5月28日的文章,本篇为转载,讲述关于JavaScript语法中的分号方面的问题。最近关于是否要去除不必要的分号的编程风格确认的时候,遇到了一些问题,很多支持去除分号的文章都引用了这一篇文章,故此这里纯粹作一个转载。">
<meta name="keywords" content="js,semicolon,asi">
<meta property="og:type" content="article">
<meta property="og:title" content="JavaScript Semicolon Insertion">
<meta property="og:url" content="https://xovel.cn/article/javascript-semicolons.html">
<meta property="og:site_name" content="清风轩">
<meta property="og:description" content="这是一篇发表于2010年5月28日的文章,本篇为转载,讲述关于JavaScript语法中的分号方面的问题。最近关于是否要去除不必要的分号的编程风格确认的时候,遇到了一些问题,很多支持去除分号的文章都引用了这一篇文章,故此这里纯粹作一个转载。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2017-03-29T14:53:00.492Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="JavaScript Semicolon Insertion">
<meta name="twitter:description" content="这是一篇发表于2010年5月28日的文章,本篇为转载,讲述关于JavaScript语法中的分号方面的问题。最近关于是否要去除不必要的分号的编程风格确认的时候,遇到了一些问题,很多支持去除分号的文章都引用了这一篇文章,故此这里纯粹作一个转载。">
<script type="text/javascript" id="hexo.configuration">
var NexT = window.NexT || {};
var CONFIG = {
scheme: 'Mist',
sidebar: {"position":"left","display":"post"},
fancybox: true,
motion: true,
duoshuo: {
userId: undefined,
author: '博主'
}
};
</script>
<title> JavaScript Semicolon Insertion | 清风轩 </title>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div style="display: none;">
<script src="https://s4.cnzz.com/z_stat.php?id=1259096563&web_id=1259096563" type="text/javascript"></script>
</div>
<div class="container one-collumn sidebar-position-left page-post-detail ">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">清风轩</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle">清风轩居 - 引仙阁</p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br />
首页
</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories" rel="section">
<i class="menu-item-icon fa fa-fw fa-th"></i> <br />
分类
</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags" rel="section">
<i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
标签
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
归档
</a>
</li>
<li class="menu-item menu-item-about">
<a href="/about" rel="section">
<i class="menu-item-icon fa fa-fw fa-user"></i> <br />
关于
</a>
</li>
<li class="menu-item menu-item-garden">
<a href="/garden" rel="section">
<i class="menu-item-icon fa fa-fw fa-life-ring"></i> <br />
花园
</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<div id="posts" class="posts-expand">
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
JavaScript Semicolon Insertion
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time itemprop="dateCreated" datetime="2017-03-29T19:47:27+08:00" content="2017-03-29">
2017-03-29
</time>
</span>
<span class="post-category" >
|
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="https://schema.org/Thing">
<a href="/categories/dev/" itemprop="url" rel="index">
<span itemprop="name">开发</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<blockquote>
<p>本文转自:<a href="http://inimino.org/~inimino/blog/javascript_semicolons" target="_blank" rel="noopener">~inimino/blog</a></p>
</blockquote>
<p><strong>JavaScript Semicolon Insertion</strong></p>
<p><strong>Everything you need to know</strong></p>
<p><em>Friday, May 28, 2010</em></p>
<hr>
<p>Automatic semicolon insertion is one of JavaScript’s most controversial syntactic features. There are also many misconceptions surrounding it.</p>
<p>自动插入分号是JavaScript里最具有争议的语法特性。其周围也存在许多误解。</p>
<p>Some JavaScript programmers use semicolons at the end of every statement, and some use them only where strictly required. Most do something in between, and a few even intentionally add extra semicolons as a matter of style.</p>
<p>一些JavaScript程序员会在每一个语句后面都使用分号,有一些则只在有必要的时候使用。大多数人介于两者之间,也有少数人将特意添加额外的分号作为一种风格。</p>
<p>Even if you use semicolons at the end of every statement, some constructs parse in non-obvious ways. Regardless of your preferences in semicolon usage, you must know the rules to write JavaScript professionally. If you remember a few simple rules, all of which are explained here, you will be able to understand how any program you might encounter will be parsed, and will be an expert on JavaScript automatic semicolon insertion, or ASI.</p>
<p>即便是在每一个语句后面都跟分号,一些构造器依然会不那么明显的进行解析。不顾后果的使用分号,你必须知道专业地编写JavaScript代码的规则。如果能记住本文提出的一些简单的规则,你可以更好的理解程序是如何的解析的,并且可以在JavaScript自动分号插入(或者简称为<code>ASI</code>)方面成为专家。</p>
<h3 id="Where-Semicolons-are-Allowed"><a href="#Where-Semicolons-are-Allowed" class="headerlink" title="Where Semicolons are Allowed"></a>Where Semicolons are Allowed</h3><p><strong>分号在哪里是允许的?</strong></p>
<p>In the formal language grammar given in the ECMAScript specification, semicolons are shown at the end of each kind of statement in which they can appear. Here is the do-while statement:</p>
<p>根据ECMAScript标准指出的常规的语法,分号在各种语句的后面都可以出现。这里有一个<code>do-while</code>语句:</p>
<pre><code>do Statement while ( Expression ) ;
</code></pre><p>Semicolons also appear in the grammar at the end of var statements, expression statements (such as “<code>4+4;</code>“ or “<code>f();</code>“), continue, return, and break statements, and throw and debugger statements.</p>
<p>分号也可以出现在<code>var</code>声明语句,表达式语句(诸如<code>4+4;</code>、<code>f();</code>),<code>continue</code>、<code>return</code>、<code>break</code>语句,以及<code>throw</code>和<code>debugger</code>语句。</p>
<p>The empty statement is just a semicolon by itself, and is a legal statement in JavaScript. For this reason, “<code>;;;</code>“ is a valid JavaScript program; it parses as three empty statements, and runs by doing nothing three times.</p>
<p>空语句同样可以使用分号,这在JavaScript中也是可行的。鉴于这个原因,<code>;;;</code>是有效的JavaScript程序,它会解析成三个空语句,运行时会做三次啥也不做的事情。</p>
<p>Sometimes empty statements are actually useful, at least syntactically. For example, to write an infinite loop, one can write <code>while(1);</code>, where the semicolon is parsed as an empty statement, which makes the while statement syntactically valid. If the semicolon was omitted, the while statement would not be complete, because a statement following the loop condition is required.</p>
<p>事实上,至少在语法构成方面,有时候空语句是很有用的。举个例子,编写一个无限循环,可以这么写:<code>while(1);</code>,分号将被解析成一个空语句,这样可以使<code>while</code>语句的语法变得有效。如果省略分号,这个<code>while</code>语句就不完整了,因为后续的循环体是必须的。</p>
<p>Finally, semicolons appear in for loops of the form <code>for ( Expression ; Expression ; Expression ) Statement</code>, and of course they may appear as themselves inside strings and regular expression literals.</p>
<p><del>太长,任性不翻译了</del></p>
<h3 id="Where-Semicolons-May-be-Omitted"><a href="#Where-Semicolons-May-be-Omitted" class="headerlink" title="Where Semicolons May be Omitted"></a>Where Semicolons May be Omitted</h3><p>In the formal grammar used in the ECMAScript specification, the semicolons are included, as described above. However, the specification prose then gives rules which describe how the actual parsing differs from the formal grammar. These are described as though semicolons are inserted into the token stream while parsing, though this is just a specification convenience; in practice, parsers do not need to generate fake semicolon tokens, but can instead regard semicolons as optional in specific places in the grammar (for an example see <a href="http://boshi.inimino.org/3box/PanPG/grammars/ECMAScript_5.peg" target="_blank" rel="noopener">this parser expression grammar for ECMAScript</a>, particularly the Statement, EOS, EOSnoLB, and SnoLB rules). Where the specification says that a semicolon is inserted, this simply means that the statement currently being parsed is ended.</p>
<p>These semicolon insertion rules are specified in section 7.9 of <a href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf" target="_blank" rel="noopener">ECMA-262 [pdf]</a>.</p>
<p>The section gives three basic rules, followed by two exceptions.</p>
<p>The rules are:</p>
<ol>
<li>When the program contains a token that is not allowed by the formal grammar, then a semicolon is inserted if (a) there is a line break at that point, or (b) the unexpected token was a closing brace.</li>
<li>When the end of a file is reached, if the program cannot be parsed otherwise, then a semicolon is inserted.</li>
<li>When a “restricted production” is encountered and contains a line terminator in a place where the grammar contains the annotation “[no LineTerminator here]”, then a semicolon is inserted.</li>
</ol>
<p>Approximately, these rules state that a statement can be terminated without a semicolon either (a) before a closing brace, (b) at the end of the program, or (c) when the next token cannot be parsed otherwise, and furthermore that there are certain places in the grammar in which, if a line break appears, it terminates the statement unconditionally. The practical effects of these rules are discussed below.</p>
<p>The exceptions are that a semicolon is never inserted as part of the header of a for loop of the form <code>for ( Expression ; Expression ; Expression ) Statement</code>, and a semicolon is never inserted if it would be parsed as an empty statement.</p>
<p>What do these rules and exceptions mean in practice?</p>
<p>Firstly, a semicolon is optional only where there is a line break, a closing brace, or the end of the program. Semicolons are not optional between statements appearing on the same line. Additionally, a semicolon is not implied at the end of a line if the first token of the subsequent line can be parsed as part of the same statement.</p>
<p><code>42; "hello!"</code> is a valid program, as is <code>42\n"hello!"</code> (with the “\n” representing an actual linebreak), but <code>42 "hello!"</code> is not; the linebreak triggers semicolon insertion but linear whitespace does not. Also valid is “<code>if(x){y()}</code>“. Here “<code>y()</code>“ is an expression statement, which can be terminated by a semicolon, but since the next token is a closing brace, the semicolon is optional even though there is no linebreak.</p>
<p>The two exceptions, for loops and empty statements, can be demonstrated together:</p>
<pre><code>for (node=getNode();
node.parent;
node=node.parent) ;
</code></pre><p>This for loop takes the parent of a node repeatedly until a node is reached which has no parent. All of this is done in the header of the for loop, so we have nothing left for the statement inside the for loop to do. However, the for loop syntax requires a statement, so we use an empty statement. Even though all three of the semicolons in this example appear at the end of a line, all three of them are required, since a semicolon is never inserted in a for loop header or to create an empty statement.</p>
<h3 id="Restricted-Productions"><a href="#Restricted-Productions" class="headerlink" title="Restricted Productions"></a>Restricted Productions</h3><p>Restricted productions are those in which a line break cannot appear in a particular position, so if a line break appears there, it will prevent the program from parsing in that way, though it may still parse another way.</p>
<p>There are five restricted productions in the grammar, they are the postfix operators <code>++</code> and <code>--</code>, continue statements, break statements, return statements, and throw statements. Break and continue statements have an optional identifier which may be used to break or continue a particular labelled loop in which the statement appears. If this feature is used, the identifier must be used on the same line as the <code>break</code> or <code>continue</code> token. The following is a valid program:</p>
<pre><code>var c,i,l,quitchars
quitchars=['q','Q']
charloop:while(c=getc()){
for (i=0; i<quitchars.length; i++){
if (c==quitchars[i]) break charloop
}
/* ... more code to handle other characters here ... */
}
</code></pre><p>If <code>getc()</code> reads a character from an input device and returns it, then the program will read characters, test each one of them to see if it is in <code>quitchars</code>, and if it is, break the input loop. Note that the labelled break statement is necessary to escape from the outer while loop and not only the inner for loop. The following program, differing only in whitespace, will also parse, but will not give the same result:</p>
<pre><code>var c,i,l,quitchars
quitchars=['q','Q']
charloop:while(c=getc()){
for (i=0; i<quitchars.length; i++){
if (c==quitchars[i])
break
charloop
}
/* ... more code to handle other characters here ... */
}
</code></pre><p>Specifically, in the latter case, the <code>charloop</code> token is not part of the break statement. Since the break statement is restricted, the linebreak at that position terminates the break statement. The <code>charloop</code> token simply parses as a reference to a charloop variable, which will never be reached, and the break statement will terminate the inner loop, not the outer loop as was intended.</p>
<p>Here are examples illustrating the other four restricted productions:</p>
<pre><code>// PostfixExpression :
// LeftHandSideExpression [no LineTerminator here] ++
// LeftHandSideExpression [no LineTerminator here] --
var i=1;
i
++;
</code></pre><p>This is a parse error, it does not parse as “<code>i++</code>“. A line terminator cannot appear before the postfix increment or decrement operator, so a “<code>++</code>“ or “<code>--</code>“ token at the start of the line will never parse as part of the preceding line.</p>
<pre><code>i
++
j
</code></pre><p>This is not a parse error: it parses as “<code>i; ++j</code>“ The pre-increment and -decrement expressions are not restricted, so a linebreak can occur between the “<code>++</code>“ or “<code>--</code>“ token and the expression which it modifies.</p>
<pre><code>// ReturnStatement: return [no LineTerminator here] Expressionopt ;
return
{i:i, j:j}
</code></pre><p>This parses as an empty return statement, followed by an expression statement which will never be reached. The following all parse as intended:</p>
<pre><code>return {
i:i, j:j}
return (
{i:i, j:j})
return {i:i
,j:j}
</code></pre><p>Note that return statements can contain linebreaks within the expression, just not between the <code>return</code> token and the start of the expression. When semicolons are intentionally omitted, it is convenient that the return statement is a restricted production, as it allows the programmer to write an empty return statement without accidentally returning the value of the next line:</p>
<pre><code>function initialize(a){
// if already initialized, do nothing
if(a.initialized) return
a.initialized = true
/* ... initialize a ... */
}
</code></pre><p>Continue and throw statements are similar to break and return:</p>
<pre><code>continue innerloop // correct
continue
innerloop; // incorrect
// ThrowStatement : throw [no LineTerminator here] Expression ;
throw // parse error
new MyComplexError(a, b, c, more, args);
// Unlike the return, break, and continue statements,
// the expression after "throw" is not optional,
// so the above will not parse at all.
throw new MyComplexError(a, b, c, more, args); // correct
throw new MyComplexError(
a, b, c, more, args); // also correct
// Any variation with 'new' and 'throw' on the same line is correct.
</code></pre><p>Note that indentation has no effect in parsing ECMAScript programs, but the presence or absence of line breaks does. Therefore, any tool that processes JavaScript source code may remove leading whitespace from lines (outside of string literals!) without changing the semantics of the program, but line breaks cannot be indiscriminately removed or replaced with spaces or semicolons. A minification tool that changes the semantics of valid programs is a broken tool, and the only way to write a correct tool is to use a complete and correct parser.</p>
<p>Line breaks following <code>return</code>, <code>break</code>, and <code>continue</code> or preceding <code>++</code> and <code>--</code> tokens can affect parsing. Since the productions above are the only restricted productions in the language, this implies that whitespace including linebreaks can be freely added anywhere else as desired to improve readability. In particular the logical, arithmetic, and string concatenation operators, the ternary or conditional operator, member access using the dot or bracket notations, function calls, and while loops, for loops, switch statements, and the rest of the control structures, can all be written with linebreaks freely used throughout.</p>
<p>As the specification says:</p>
<blockquote>
<p>The resulting practical advice to ECMAScript programmers is: A postfix <code>++</code> or <code>--</code> operator should appear on the same line as its operand. An Expression in a return or throw statement should start on the same line as the return or throw token. A Identifier in a break or continue statement should be on the same line as the break or continue token.</p>
</blockquote>
<p>The most commonly cited programmer error related to restricted productions is to put the return value on the line after the <code>return</code> token, especially common when the returned value is a large object or array literal or multiline string. Line break errors with postfix operators, break, continue, and throw statements are rarely seen in practice, for the simple reason that the erroneous line breaks look unnatural to most programmers and so are unlikely to be written.</p>
<p>The final subtlety of ASI arises from the first rule, which requires that the program contain a token which is not allowed by the formal grammar, before a semicolon will be inserted. When writing code with optional semicolons omitted, it is important to keep this rule in mind so that required semicolons are not inadvertently omitted as well. This rule is what makes it possible to extend statements across multiple lines, as in the following examples:</p>
<pre><code>return obj.method('abc')
.method('xyz')
.method('pqr')
return "a long string\n"
+ "continued across\n"
+ "several lines"
totalArea = rect_a.height * rect_a.width
+ rect_b.height * rect_b.width
+ circ.radius * circ.radius * Math.PI
</code></pre><p>The rule considers only the first token of the following line. If that token can parse as part of the statement, then the statement is continued (even if parsing fails a little further on: the language syntax is designed to only require one token of lookahead in the parser). If the first token cannot extend the statement, then a new statement begins (which the spec describes by saying a semicolon is inserted).</p>
<p>The potential for error arises whenever there is a pair of statements A and B such that both A and B are valid statements standing alone, but the first token of B can also be accepted as an extension of A. In such cases, if a semicolon is not provided, the parser will not parse B as a separate statement, and will either reject the program or parse it in a way that the programmer did not intend. Thus when semicolons are omitted, the programmer must beware any such statement pair separated by a linebreak as:</p>
<pre><code>A
B
</code></pre><p>Where B begins with a token that would be accepted by the parser if it had appeared at the end of line A.</p>
<p>The majority of JavaScript statements begin with an identifier, and the majority of the remainder begin with a keyword such as “var”, “function”, or “if”. For any such statement B beginning with a keyword or identifier, as well as any beginning with a string or number literal, there is no valid complete statement A such that the first token of B would be accepted by the parser as extending A. (The verification of this from the grammar is left as an exercise for the reader.)</p>
<pre><code>A
function f(x){return x*x}
// for any statement A, without any terminating semicolon,
// all of these examples will parse as intended
A
f(7)
A
"a string".length
</code></pre><p>Unfortunately, there are five tokens that can appear both at the start of a statement, and as an extension of some complete statement A. These tokens are the open parenthesis “<code>(</code>“, open square brace “<code>[</code>“, slash or solidus “<code>/</code>“, and “<code>+</code>“ and “<code>-</code>“. Of these, the first two are problematic in practice.</p>
<p>This means it is not always the case that a line break can replace a semicolon between statements.</p>
<p>The spec gives the following example:</p>
<blockquote>
<pre><code>a = b + c
(d + e).print()
</code></pre><p>is not transformed by automatic semicolon insertion, because the parenthesised expression that begins the second line can be interpreted as an argument list for a function call:</p>
<pre><code>a = b + c(d + e).print
</code></pre></blockquote>
<p>The spec goes on to suggest, “In the circumstance that an assignment statement must begin with a left parenthesis, it is a good idea for the programmer to provide an explicit semicolon at the end of the preceding statement rather than to rely on automatic semicolon insertion.” A more robust alternative where semicolons are intentionally omitted is to include the semicolon at the beginning of the line, directly before the token that introduces the potential ambiguity:</p>
<pre><code>a = b + c
;(d + e).print()
</code></pre><p><del>上面的代码前面有很多空格,实际上在marked工具解析的时候会忽略代码前的空格</del></p>
<p>Statements beginning with open parentheses or square braces are somewhat rare, but do arise in practice.</p>
<p>Examples involving open square braces are more common now that “functional” operations such as map, filter, and forEach are common on arrays. It is often convenient to use an array literal with a forEach call which is evaluated for its side-effects, as in the following:</p>
<pre><code>[['January','Jan']
,['February','Feb']
,['March','Mar']
,['April','Apr']
,['May','May']
,['June','Jun']
,['July','Jul']
,['August','Aug']
,['September','Sep']
,['October','Oct']
,['November','Nov']
,['December','Dec']
].forEach(function(a){ print("The abbreviation of "+a[0]+" is "+a[1]+".") })
['/script.js'
,'/style1.css'
,'/style2.css'
,'/page1.html'
].forEach(function(uri){
log('Looking up and caching '+uri)
fetch_and_cache(uri)})
</code></pre><p>In cases where array literals are used for their value in an assignment expression, or are passed to a function, they will not appear as the beginning of the statement, so an opening square brace as the first token is rare, but does occur.</p>
<p>The final troublesome token is the slash, and this one can be highly counterintuitive. Consider the following example:</p>
<pre><code>var i,s
s="here is a string"
i=0
/[a-z]/g.exec(s)
</code></pre><p>On lines 1-3 we set up some variables, and on line 4, it appears, we construct a regexp literal <code>/[a-z]/g</code> which will globally match a-z, and then we evaluate this regexp against the string with the exec method. Since the return value of the exec() call is not used, this code is not very useful, but we might expect it to compile. However, the slash can not only appear at the beginning of a regexp literal, but also serves as the division operator. That means that the leading slash on line 4 will actually be parsed as a continuation of the assignment statement on the previous line. The entirety of lines three and four parses as the single statement “i equals 0 divided by [a-z] divided by g.exec(s)”.</p>
<p>This issue almost never arises in practice because there is seldom a practical reason to begin a statement with a regexp literal. In the example above, the value of the exec() call would usually be passed to a function or assigned to a variable, and in either case the line would no longer begin with a slash. One possible exception is, again, with the forEach Array method, which could be usefully used on the return value of an exec() call directly.</p>
<p>The operators “<code>+</code>“ and “<code>-</code>“ can be used as unary operators, to convert a value to the Number type and in the case of “<code>-</code>“ to reverse the sign. If used at the beginning of a statement with semicolons omitted, these can be interpreted as the corresponding binary operator, as a continuation of the previous statement. Even when semicolons are intentionally omitted, this is rarely a problem, as a leading unary operator is even less likely than a regexp literal to occur as the first token of a statement (and it does not look self-contained in the way that a parenthesized expression does). As with regexps, if the programmer wanted to coerce a value to a Number, it was probably to do something with that Number value, such as assign it to a variable or pass it to a function, and in either case the unary operator would not be the first token of the statement:</p>
<pre><code>var x,y,z
x = +y; // useful
y = -y; // useful
print(-y); // useful
+z; // useless
</code></pre><p>In all such cases, when semicolons are omitted, the safest practice with lines beginning with an open parenthesis or square brace is to precede the token with a semicolon on the line itself. This advice also applies in the unlikely case of statements beginning with an arithmetic operator “<code>+</code>“, “<code>-</code>“, or “<code>/</code>“. In this way, even when semicolons are not used elsewhere, the line will be protected from misparsing regardless of how the line previous to it may change over time.</p>
<h3 id="Misconceptions"><a href="#Misconceptions" class="headerlink" title="Misconceptions"></a>Misconceptions</h3><p>Many new JavaScript programmers are advised to just use semicolons everywhere, and expect that if they do not intentionally use the semicolon insertion rules, they can safely ignore the existence of this entire language feature. This is not the case, because of the restricted productions described above, notably the return statement. When becoming aware of the restricted production issue, programmers may then become overly wary of linebreaks, and avoid them even when they would increase clarity. It is best to be familiar with all the rules for ASI so as to be able to read any code regardless of how it is written, and to write code that is as clear as it can be.</p>
<p>Another misconception is that bugs in browser JavaScript engines mean that using semicolons everywhere is safer, and will protect the developer from compatibility issues between browsers. This is simply not the case. All extant browsers implement the specification correctly with regard to ASI, and any bugs that may have existed are long since lost in the mists of early Web history. There is no reason to be concerned about browser compatibility in regard to semicolon insertion: all browsers implement the same rules and they are the rules given by the spec and explained above.</p>
<h3 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h3><p>Should you omit optional semicolons or not? The answer is a matter of personal preference, but should be made on the basis of informed choice rather than nebulous fears of unknown syntactical traps or nonexistent browser bugs. If you remember the rules given here, you are equipped to make your own choices, and to read any JavaScript easily.</p>
<p>If you choose to omit semicolons where possible, my advice is to insert them immediately before the opening parenthesis or square bracket in any statement that begins with one of those tokens, or any which begins with one of the arithmetic operator tokens “<code>/</code>“, “<code>+</code>“, or “<code>-</code>“ if you should happen to write such a statement.</p>
<p>Whether you omit semicolons or not, you must remember the restricted productions (return, break, continue, throw, and the postfix increment and decrement operators), and you should feel free to use linebreaks everywhere else to improve the readability of your code.</p>
<p><del>全文完</del></p>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-tags">
<a href="/tags/js/" rel="tag">#js</a>
<a href="/tags/semicolon/" rel="tag">#semicolon</a>
<a href="/tags/asi/" rel="tag">#asi</a>
</div>
<div class="post-nav">
<div class="post-nav-next post-nav-item">
<a href="/article/record-to-two-special-weddings.html" rel="next" title="两次婚礼纪实">
<i class="fa fa-chevron-left"></i> 两次婚礼纪实
</a>
</div>
<div class="post-nav-prev post-nav-item">
<a href="/article/brackets-emmet.html" rel="prev" title="Brackets的使用与emmet插件安装的一个问题">
Brackets的使用与emmet插件安装的一个问题 <i class="fa fa-chevron-right"></i>
</a>
</div>
</div>
</footer>
</article>
<div class="post-spread">
</div>
</div>
</div>
<div class="comments" id="comments">
</div>
</div>
<div class="sidebar-toggle">
<div class="sidebar-toggle-line-wrap">
<span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
</div>
</div>
<aside id="sidebar" class="sidebar">
<div class="sidebar-inner">
<ul class="sidebar-nav motion-element">
<li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
文章目录
</li>
<li class="sidebar-nav-overview" data-target="site-overview">
站点概览
</li>
</ul>
<section class="site-overview sidebar-panel ">
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
<img class="site-author-image" itemprop="image"
src="/images/xovel.jpg"
alt="xovel" />
<p class="site-author-name" itemprop="name">xovel</p>
<p class="site-description motion-element" itemprop="description">缓缓之间,风云静变</p>
</div>
<nav class="site-state motion-element">
<div class="site-state-item site-state-posts">
<a href="/archives">
<span class="site-state-item-count">73</span>
<span class="site-state-item-name">日志</span>
</a>
</div>
<div class="site-state-item site-state-categories">
<a href="/categories">
<span class="site-state-item-count">18</span>
<span class="site-state-item-name">分类</span>
</a>
</div>
<div class="site-state-item site-state-tags">
<a href="/tags">
<span class="site-state-item-count">177</span>
<span class="site-state-item-name">标签</span>
</a>
</div>
</nav>
<div class="feed-link motion-element">
<a href="/atom.xml" rel="alternate">
<i class="fa fa-rss"></i>
RSS
</a>
</div>
<div class="links-of-author motion-element">
<span class="links-of-author-item">
<a href="https://github.com/xovel" target="_blank" title="github">
<i class="fa fa-fw fa-github"></i>
github
</a>
</span>
<span class="links-of-author-item">
<a href="https://hdk4.com" target="_blank" title="hdk4.com">
<i class="fa fa-fw fa-adjust"></i>
hdk4.com
</a>
</span>
</div>
<div class="cc-license motion-element" itemprop="license">
<a href="http://creativecommons.org/licenses/by-nc-sa/4.0" class="cc-opacity" target="_blank">
<img src="/images/cc-by-nc-sa.svg" alt="Creative Commons" />
</a>
</div>
</section>
<section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
<div class="post-toc">
<div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#Where-Semicolons-are-Allowed"><span class="nav-number">1.</span> <span class="nav-text">Where Semicolons are Allowed</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Where-Semicolons-May-be-Omitted"><span class="nav-number">2.</span> <span class="nav-text">Where Semicolons May be Omitted</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Restricted-Productions"><span class="nav-number">3.</span> <span class="nav-text">Restricted Productions</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Misconceptions"><span class="nav-number">4.</span> <span class="nav-text">Misconceptions</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Conclusion"><span class="nav-number">5.</span> <span class="nav-text">Conclusion</span></a></li></ol></div>
</div>
</section>
</div>
</aside>
</div>
</main>
<footer id="footer" class="footer">
<div class="footer-inner">
<div class="copyright" >
© 2016 -
<span itemprop="copyrightYear">2021</span>
<span class="with-love">
<i class="fa fa-heart"></i>
</span>
<span class="author" itemprop="copyrightHolder">xovel</span>
</div>
<div class="powered-by">
由 <a class="theme-link" href="http://hexo.io" rel="external nofollow" target="_blank">Hexo</a> 强力驱动
</div>
<div class="theme-info">
主题 -
<a class="theme-link" href="https://github.com/iissnan/hexo-theme-next" rel="external nofollow" target="_blank">
NexT.Mist
</a>
</div>
</div>
</footer>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
</div>
</div>
<script type="text/javascript">
if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
window.Promise = null;
}
</script>
<script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
<script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
<script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
<script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
<script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
<script type="text/javascript" src="/js/src/utils.js?v=5.0.1"></script>
<script type="text/javascript" src="/js/src/motion.js?v=5.0.1"></script>
<script type="text/javascript" src="/js/src/scrollspy.js?v=5.0.1"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.0.1"></script>
<script type="text/javascript" src="/js/src/bootstrap.js?v=5.0.1"></script>
</body>
</html>