js代码在什么位置可以换行

一行过长的代码会影响阅读体验. 实际项目中,我们往往把过长的代码分成多行去写.
比如在js中, promise链经常要分成多行写. 对此本人想到一个问题, 就是js中, 在什么地方换行是合法并且不破坏原有代码逻辑的.

探究这个问题过程中本人造了一个小玩具, breakline, 顾名思义, 它的作用就是在不破话代码的逻辑的情况下把js代码拆成多行. 项目地址:https://github.com/flowmemo/breakline

我们接下来还是谈换行的问题. 为了简单起见, 本文不讨论注释中的换行符.

换行符的作用

在一般的语言中, 换行符一般是意味着一个statement1的结束, 分号也通常有这个作用. 但是js中换行符并没有代表statement的结束的意思. 事实上在规范中, 换行符(规范中称为line terminator)在多数情况下和空格、tab的作用是一样的, 是为了分隔token以及方便阅读, 除了…

  1. 换行符在某些情况下会触发自动分号插入(Automatic Semicolon Insertioin, 以下简称ASI)
  2. 换行符本身是token的一部分, 比如在StringLiteral(单/双引号字符串, 必须通过\来escape), Template和TemplateSubstitutionTail(这两个就是`包起来的字符串)中.

以下为ECMA-262 7.0(也就是ES7、ES2016)规范中相应的描述:

A line terminator cannot occur within any token except a StringLiteral, Template, or TemplateSubstitutionTail. Line terminators may only occur within a StringLiteral token as part of a LineContinuation.

有了这些信息, 我们就应该知道在什么情况下可以换行了:只要你的新加的换行符不在上述的两个规则内就行.

我们再继续探究这两个规则.

ASI

EMCA规范中有一部分专门讲了ASI, 在http://www.ecma-international.org/ecma-262/7.0/index.html#sec-automatic-semicolon-insertion

对于ASI这里就不展开细讲了, 这里只说ASI和换行相关的部分.

Offending Token

一个情况是, 换行符后的token和之前的token一起进行parse会出错, 那么换行符后的token叫做offend token. Offend token前会自动插入一个分号.
举例:

var foo
foo = 2

一共5个token, 按先后顺序是var, foo, foo, =, 2. parser先解析了前两个token, 很好, 没错, 然后接着parse下一个token foo, 出错了(因为没有一个js语法中并没有一条production包含var foo foo的形式). 于是ASI被触发, 在换行符和offend token间加了个分号. var foo就被parse成了Variable Statement, 然后后面的foo = 2也被成功parse了2.

Restricted Production

Restricted production规定了触发ASI的几种特殊情况.

以下几个位置出现换行符, 即使之后的token可以继续被合法parse, 也会触发ASI

  1. 后缀自增/自减运算符之前
  2. continue, break, return, throw, yield之后
  3. arrow function的=>之前

所以我们要尽量避免在上述位置加换行符(除非你知道自己是在做什么).

Template和TemplateSubstitutionTail

这部分简而言之就是不要在表示字符串内容的部分换行. 这个很好理解了.
需要注意的是对于有替换的部分, 也就是${}的花括号内部,是可以当作普通的expression换行的
也就是说

var s = `hello ${person.name}!`

var s = `hello ${
person
.
name
}!`

是相同的.

玩具:breakline

在探究这个过程中本人写了breakline. 这个工具的目的就是把你的js代码拆成多行, 同时又不对代码的功能和逻辑造成实质的影响.

总结

在实际写代码过程中, 符合普通人逻辑的“正常”的换行是没有问题的. 如果说有需要注意的地方,也就是restricted production的第二条了, continue, break, return, throw, yield这几个关键字以及=>前不要换行, 其他的地方换行的话…可以说也没什么美感了, 一般人不会那么干的.


[1] 关于statement和expression的区别, 可以参考http://www.2ality.com/2012/09/expressions-vs-statements.html
[2] 实际上在foo = 2后也自动插入了一个分号.