JavaScript分号自动插入机制

这片博客主要总结(翻译…)下JavaScript语言中的分号自动插入机制 ASI,以及表达式Expression和声明 statement的概念和区别。从后两者开始说起:

1、Expression和statement的关系和区别

An expression is any valid unit of code that resolves to a value

Statements is everything that “does something”. A program is always a sequence of statements

一个Expression是任意可以解析为一个值的,合法的代码片段;

一个Statements也是JavaScript完成逻辑的组成片段,可以由expression和合法的JavaScript语法组成,不要求必须返回一个值

区别突出在expression重在可以转化为一个值,而statement重在完成一个逻辑,这个逻辑可以由很多expression和其他语法构成。比如for语句,无法转化成一个值,不是expression,但是却是一个statement;任意一个expression后面加个分号也都可以看做是一个statement(尼玛有点绕…)。

statements必须以分号作为结尾(除了特殊几个,如下2-2);而expression末尾加上分号也就变成了statements。由于提到expression并没有强制要求以分号结尾,故这里得说明一下JavaScript的Auto Semicolon Insertion (ASI)分号自动插入机制。

2、Auto Semicolon Insertion (ASI)分号自动插入机制

Semicolon Insertion只是一个术语,并不意味着在代码解析阶段真实插入分号,只是用来说明写不写分号是可选的而非必须的。知乎上对加还是不加这个问题的讨论还是蛮激烈的…

2.1 statements组成

js解析器会尽可能把新的符号作为当前statements的一部分除非遇到了分号,比如:

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
例1:
var a=1
console.log(a)
这里a=1后面并没有加分号,但整个语句可以执行,因为1后面跟console是非法的,所以解析器智能的将如上代码以如下的方式对待:
var a=1;
console.log(a)
例2:
由此也引发了一些问题,比如说如下的demo:
a = b + c
(d + e).print()
这里解析器会将c作为一个function,function后加括号调用是合法的,并且传入d+e作为参数调用,最终将其对待为:
a = b + c(d + e).print()
如果本意是a=b+c为一个语句,那么不加分号情况下的执行结果就会出错,类似的情况还有如下几个:
例3:
a = b
/hi/g.exec(c).map(d)
b后如果不加分号解析器会解析为:
a = b / hi / g.exec(c).map(d)
按除法对待,如果本意是下面那句是个正则的操作,那么执行结果也会报错
例4:
var foo = "bar"
[ "red", "green" ].foreach(function(c) { console.log(c) })
"bar"后不加分号解析器将后面分号作为obj[]般调用对象的属性一样对待,解析为:
var foo = "bar"[ "red", "green" ].foreach(function(c) { console.log(c) })
执行结果报错
总结:在没有分号情况下,解析器将语法上合法的字符尽可能的并入一个statement中,如上例2,3,4的情况一定要加上分号使之成为一个statement来提示解析器正确解析以便得出预期的结果

由以上栗子引出了ASI的触发时机:
当引入换行符且跟随的字符无法添加到当前statement中时(即如果加了后面字符会导致语法上非法,如例1),换行处按自动添加分号对待。

2.2 不强制加引号的statements

  • Loops: for, while (not do-while)
  • Branching: if, switch, try
  • Function declarations (not function expressions)

以上来自Axel博文,这部分没发现什么卵用

2.3 以下语法后加换行时按自动插入分号对待

  • 位于左侧的++、–操作符加空格
  • continue + 空格
  • break + 空格
  • return + 空格
  • throw + 空格
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    例1:
    a
    ++
    c
    按如下对待:
    a;
    ++
    c
    即++、--这俩前缀操作符会避免对前一行的影响
    对于 continue, break, return, throw这几个关键字如果后面没有字符了且没有分号,解析器认为不应该对下一行产生影响,按直接插入分号对待
    例2:
    return
    a+b
    对待为:
    return;
    a+b

3.总结一下

  1. 不要将++,–和它们的操作数分行放
  2. return, throw, break, continue后面如果有参数不要另起一行
  3. ([/这三个作为行首时,前面加分号以免解析器按与上一行相关的字符对待

4.expression都有哪些

概念上来讲,表达式(代码片段)包括两类:

  • 变量赋值 如 x = 5;
  • 能代表一个值 如4+5

javascript语言把表达式分为以下几类:

  • 算数运算

    计算结果为数值(1,2.4 …),包括使用运算符运算: +、-、、%、/、*(幂)、++、–、

  • String
    计算结果为字符串(”a”…):包括用字符串连接符+的,如”a”+”b”

  • 逻辑运算
    计算结果为boolean:用逻辑运算符进行运算的, 请留意逻辑运算返回值那部分…

  • 原始表达式
    javascript中的关键字(this…)及常规的表达式,如()括号等

  • Left-hand-side expressions(左端符?尼玛真不知如何翻译)
    如new,super,Spread operator(传播符,可以表示为数组)

能强行归为以上类型的就算是javascript表达式了吧。。。

原文