这部分描述了JavaScript的词法。ECMAScript源码文本会被从左到右扫描，并被转换为一系列的输入元素，包括tokens、控制符、行终止符、注释和空白符。ECMAScript定义了一些关键字、字面量以及行尾分号补全的规则。

## Unicode 格式控制符

Unicode 格式控制符用于控制对源码文本的解释，但是并不会显示出来。

Unicode编码表示的格式控制符
代码点 | 名称 | 缩写 | 说明
--- | --- | --- | ---
`U+200C` | 零宽度非结合子 | <ZWNJ> | 放置在一些经常会被当成连字的字符之间，用于将它们分别以独立形式显示（Wikipedia）
`U+200D` | 零宽度结合子 | <ZWJ> | 放置在一些通常不会被标记为连字的字符之间，用于将这些字符以连字形式显示（Wikipedia）
`U+FEFF` | 字节流方向标识 | <BOM> | 在脚本开头使用，除了将脚本标记为Unicode格式以外，还用来标记文本的字节流方向（Wikipedia）

## 空白符

空白符提升了源码的可读性，并将标记 (tokens) 区分开。这些符号通常不影响源码的功能。通常可以用压缩器来移除源码中的空白，减少数据传输量。

空白符
代码点 | 名称 | 缩写 | 说明 | 转义序列
--- | --- | --- | --- | ---
U+0009 | 制表符 | <HT> | 水平制表符 | \t
U+000B | 垂直制表符 | <VT> | 垂直制表符 | \v
U+000C | 分页符 | <FF> | 分页符（Wikipedia） | \f
U+0020 | 空格 | <SP> | 空格 |
U+00A0 | 无间断空格 | <NBSP> | 在该空格处不会换行 |
Others | 其他Unicode空白 | <USP> | Wikipedia上对Unicode空白的介绍 |

## 行终止符

除了空白符之外，行终止符也可以提高源码的可读性。不同的是，行终止符可以影响JavaScript代码的执行。行终止符也会影响自动分号补全的执行。在正则表达式中，行终止符会被\s匹配。

在ECMAScript中，只有下列Unicode字符会被当成行终止符，其他的行终止符（比如Next Line、NEL、U+0085等）都会被当成空白。

行终止符
编码 | 名称 | 缩写 | 说明 | 转义序列
--- | --- | --- | --- | ---
U+000A | 换行符 | <LF> | 在UNIX系统中起新行 | \n
U+000D | 回车符 | <CR> | 在Commodore和早期的Mac系统中起新行 | \r
U+2028 | 行分隔符 | <LS> | Wikipedia |
U+2029 | 段分隔符 | <PS> | Wikipedia |

## 注释

注释用来在源码中增加提示、笔记、建议、警告等信息，可以帮助阅读和理解源码。在调试时，可以用来将一段代码屏蔽掉，防止其运行。

JavaScript中有两种生成注释的方法。

第一种是单行注释 (single-line comment)，使用`//`，会将该行中符号以后的文本都视为注释：

    function comment() {
      // 这是行注释
      console.log("Hello world!");
    }
    comment();

第二种是多行注释 (multiple-line comment)，使用`/* */` ，这种方式更加灵活：

比如，可以使用多行注释来实现单行注释：

    function comment() {
      /* 单行注释 */
      console.log("Hello world!");
    }
    comment();

也可以用来实现多行注释：

    function comment() {
      /* 多行注释，
         直到终止符号才结束 */
      console.log("Hello world!");
    }
    comment();

多行注释也可以用于单行间注释，这样会造成代码可读性变差，所以要谨慎使用：

    function comment(x) {
      console.log("Hello " + x /* 引入x的值 */ + " !");
    }
    comment("world");

另外，块注释也可以用来屏蔽一段代码，只要将这段代码用块注释包裹起来就可以了：

    function comment() {
      /* console.log("Hello world!"); */
    }
    comment();

注释中的`console.log()`的调用始终无效。这种方式可以屏蔽任意多行的代码。

## 关键字

### ECMAScript 6中的保留关键字

- `break`
- `case`
- `catch`
- `class`
- `const`
- `continue`
- `debugger`
- `default`
- `delete`
- `do`
- `else`
- `export`
- `extends`
- `finally`
- `for`
- `function`
- `if`
- `import`
- `in`
- `instanceof`
- `new`
- `return`
- `super`
- `switch`
- `this`
- `throw`
- `try`
- `typeof`
- `var`
- `void`
- `while`
- `with`
- `yield`

### 未来保留关键字

在ECMAScript规格中，这些关键字被当成关键字保留。目前它们没有特殊功能，但是在未来某个时间可能会加上。所以这些关键字不能当成标识符使用。这些关键字在严格模式和非严格模式中均不能使用。

- `enum`

以下关键字只在严格模式中被当成保留关键字：

- `implements`
- `interface`
- `let`
- `package`
- `private`
- `protected`
- `public`
- `static`

以下关键字只在模块代码中被当成保留关键字：

- `await`

#### 之前标准中的保留关键字

这里是之前版本中的ECMAScript（1到3）中的保留关键字：

- `abstract`
- `boolean`
- `byte`
- `char`
- `double`
- `final`
- `float`
- `goto`
- `int`
- `long`
- `native`
- `short`
- `synchronized`
- `transient`
- `volatile`

另外，直接量`null`、`true`和`false`同样不能被当成标识使用。

### 保留字的使用

事实上保留字是仅针对标识符(Identifier)的文法定义而言的(而非标识符名(IdentifierName)的文法定义). 如 es5.github.com/#A.1中所描述的, 这些都是不排斥保留字的标识符名.

    a.import
    a["import"]
    a = { import: "test" }.

另一方面，如下用法是不允许的。因为它是一个标识符，而标识符的文法定义是除保留字以外的标识符名。标识符用于函数声明式和函数表达式.

    function import() {} // Illegal.

## 直接量

### 空直接量

`更多信息可以参考null`

    null

### 布尔直接量

更多信息可以参考`Boolean`

    true
    false

### 数值直接量

#### 十进制

    1234567890
    42

    // 谨慎使用0开头的数值

    0888 // 转换为十进制888
    0777 // 转换为八进制777，十进制511

请注意，十进制数值直接量可以以0开头，但是如果0以后的最高位比8小，数值将会被认为是八进制而不会报错。更多信息可以参考bug 957513和`parseInt()`。

#### 二进制

二进制表示为开头是0后接大写或小写的B（`0b`或者`0B`）。这是ECMAScript 6中的新语法，可以参考下面的浏览器兼容性表格。如果`0b`之后有除了0或1以外的数字，将会抛出`SyntaxError`：“Missing binary digits after 0b”。

    var FLT_SIGNBIT  = 0b10000000000000000000000000000000; // 2147483648
    var FLT_EXPONENT = 0b01111111100000000000000000000000; // 2139095040
    var FLT_MANTISSA = 0B00000000011111111111111111111111; // 8388607

#### 八进制

八进制表示为开头是0后接大写或小写的O（`0o`或`0O`）。这是ECMAScript 6中的新语法，可以参考下面的浏览器兼容性表格。如果有不在（01234567）中的数字，将会抛出`SyntaxError`：“Missing octal digits after 0o”。

    var n = 0O755; // 493
    var m = 0o644; // 420

    // 用0开头也可以实现（请查看上方十进制有关部分）
    0755
    0644

#### 十六进制

十六进制表示为开头是0后接大写或小写的X（`0x`或`0X`）。如果有不在（0123456789ABCDEF）中的数字，将会抛出`SyntaxError`：“Identifier starts immediately after numeric literal”。

    0xFFFFFFFFFFFFFFFFF // 295147905179352830000
    0x123456789ABCDEF   // 81985529216486900
    0XA                 // 10

### 对象直接量

更多信息可以参考`Object`和对象初始化器。

    var o = { a: "foo", b: "bar", c: 42 };

    // ES6中的简略表示方法
    var a = "foo", b = "bar", c = 42;
    var o = {a, b, c};
    // 不需要这样
    var o = { a: a, b: b, c: c };

### 数组直接量

更多信息可以参考`Array`。

    [1954, 1974, 1990, 2014]

### 字符串直接量

    'foo'
    "bar"

#### 十六进制转义序列

    '\xA9' // "©"

#### Unicode转义序列

Unicode转义序列要求在`\u`之后至少有四个字符。

    '\u00A9' // "©"

#### Unicode编码转义

ECMAScript 6新增特性。使用Unicode编码转义，任何字符都可以被转义为十六进制编码。最高可以用到`0x10FFFF`。使用单纯的Unicode转义通常需要写成分开的两半以达到相同的效果。

可以参考`String.fromCodePoint()`和`String.prototype.codePointAt()`。

    '\u{2F804}'

    // 使用单纯Unicode转义
    '\uD87E\uDC04'

### 正则表达式直接量

更多信息可以参考`RegExp`。

    /ab+c/g

    // 一个空的正则表达式直接量
    // 必须有一个空的非捕获分组
    // 以避免被当成是行注释符号
    /(?:)/

### 模板直接量

更多信息可以参考template strings。

    `string text`

    `string text line 1
     string text line 2`

    `string text ${expression} string text`

    tag `string text ${expression} string text`

## 自动分号补全

一些JavaScript语句必须用分号结束，所以会被自动分号补全 (ASI)影响：

- 空语句
- `let`、`const`、变量声明
- `import`、`export`、模块定义
- 表达式语句
- `debugger`
- `continue`、`break`、`throw`
- `return`

ECMAScript规格提到自动分号补全的三个规则。

1. 当出现一个不允许的行终止符或“}”时，会在其之前插入一个分号。

    { 1 2 } 3

    // 将会被ASI转换为

    { 1 2 ;} 3;

2. 当捕获到标识符输入流的结尾，并且无法将单个输入流转换为一个完整的程序时，将在结尾插入一个分号。

在下面这段中，由于在`b`和`++`之间出现了一个行终止符，所以`++`未被当成变量`b`的后置运算符。

    a = b
    ++c

    // 将被ASI转换为

    a = b;
    ++c;

3. 当语句中包含语法中的限制产品后跟一个行终止符的时候，将会在结尾插入一个分号。带“这里没有行终止符”规则的语句有：

- 后置运算符（`++`和`--）`
- `continue`
- `break`
- `return`
- `yield`, `yield*`
- `module`

    return
    a + b

    // 将被ASI转换为

    return;
    a + b;

## 规格

规格 | 状态 | 备注
--- | --- | ---
ECMAScript 1st Edition (ECMA-262) | Standard | 初始定义
ECMAScript 5.1 (ECMA-262) Lexical Conventions | Standard |
ECMAScript 2015 (6th Edition, ECMA-262) Lexical Grammar | Standard | 增加：二进制和八进制数值直接量，Unicode编码转义直接量、模板直接量

## 浏览器兼容

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

- Desktop
- Mobile

特性 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari
--- | --- | --- | --- | --- | ---
基础支持 | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
二进制和八进制数值 | 41 | 25 (25) | ? | 28 | ?
Unicode编码转义 (`\u{}`) | ? | 40 (40) | ? | ? | ?
对象直接量的简易表示 | 未实现 | 33 (33) | 未实现 | 未实现 | 未实现
模板直接量 | 未实现 | 34 (34) | 未实现 | 未实现 | 未实现

特性 | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile
--- | --- | --- | --- | --- | --- | ---
基础支持 | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
二进制和八进制数值 | ? | 41 | 33.0 (33) | ? | ? | ?
Unicode编码转义 (`\u{}`) | ? | ? | 40.0 (40) | ? | ? | ?
对象直接量的简易表示 | 未实现 | 未实现 | 33.0 (33) | 未实现 | 未实现 | 未实现
模板直接量 | 未实现 | 未实现 | 34.0 (34) | 未实现 | 未实现 | 未实现

## Firefox特殊提示

- 在Firefox 5（JavaScript 1.8.6）之前的版本上，储备关键字可以在非严格模式中被使用。这个问题在Firefox 5中被修复。

## See also

- Jeff Walden: Binary and octal numbers
- Mathias Bynens: JavaScript character escape sequences
- `Boolean`
- `Number`
- `RegExp`
- `String`