这部分描述了JavaScript的词法。ECMAScript源码文本会被从左到右扫描,并被转换为一系列的输入元素,包括tokens、控制符、行终止符、注释和空白符。ECMAScript定义了一些关键字、字面量以及行尾分号补全的规则。
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
另外,直接量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
对象直接量
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编码转义直接量、模板直接量 |
浏览器兼容
特性 | 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中被修复。