词法文法

这部分描述了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中的保留关键字

未来保留关键字

在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

另外,直接量nulltruefalse同样不能被当成标识使用。

保留字的使用

事实上保留字是仅针对标识符(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 957513parseInt()

二进制

二进制表示为开头是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(0o0O)。这是ECMAScript 6中的新语法,可以参考下面的浏览器兼容性表格。如果有不在(01234567)中的数字,将会抛出SyntaxError:“Missing octal digits after 0o”。

var n = 0O755; // 493
var m = 0o644; // 420
// 用0开头也可以实现(请查看上方十进制有关部分)
0755
0644

十六进制

十六进制表示为开头是0后接大写或小写的X(0x0X)。如果有不在(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)影响:

  • 空语句
  • letconst、变量声明
  • importexport、模块定义
  • 表达式语句
  • debugger
  • continuebreak、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编码转义直接量、模板直接量

浏览器兼容

特性 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

文档标签和贡献者