编程语言的词法结构是一套基本规则,规定了如何使用这门语言编写程序,规定了变量如何命名、注释的定界符、如何分隔程序的语句等

ECMAScript 源码文本会被从左到右扫描,并被转换为一系列的输入元素(Token),包括标识符、控制符、行终止符、注释和空白符

JavaScript 代码的文本

字符集

JavaScript 代码使用 [[#Unicode]] 字符集

区分大小写

空白字符

JavaScript 会忽略程序中标识(Token)之间的空格,多数情况下同样会忽略换行符。因此可以采用整齐、一致的缩进、空格等来形成统一的格式,从而提高代码的可读性

可打印字符在渲染时会产生输出,但空白字符不会。空白字符在有限程度上定义了文本的布局,如:

常见的识别为空格的空白字符

名称 Unicode 转义序列 转义符 HTML/XML命名实体 说明
水平制表符 \u0009 \t &Tab; <HT>
空格符 \u0020 普通空格
非中断空格符 \u00A0 &nbsp;&NonBreakingSpace; 与空格相同,但不是可以断行的点
零宽空格 \u200B &ZeroWidthSpace; <ZWSP>,不使用显式空格向文本处理系统指示单词边界
零宽非连接符 \u200C &zwnj; <ZWNJ>,中断彼此相邻的字符关联
零宽连接器 \u200D &zwj; <ZWJ>,使左右字符关联
非中断零宽空格 \u2060 &NoBreak; <WJ>,与零宽空格相同,但不是可以断行的点

常见的行终止符

名称 Unicode 转义序列 转义符 HTML/XML 命名实体 说明
换行符 \u000A \n &NewLine; <LF>
垂直制表符 \u000B \v <VT>
进纸符 \u000C \f <FF>
回车符 \u000D \r <CR>
下一行 \u0085 <NEL>
行分隔符 \u2028 <LS>
段落分割符 \u2029 <PS>

注释

注释不会被执行,是给阅读代码的人看的

可以添加注释来对代码进行解释、提高代码的可读性,或者将代码注释,以阻止其执行

单行注释

// 注释内容

// 可以独占一行
const a = 6 // 也可以放到代码行末

多行注释

/* 注释内容
注释内容
注释内容 */

const a = /* 可以在代码中间 */ 6

为了美观,常写成:

/**
 * 注释内容
 * 注释内容
 * 注释内容
 */

块级注释左部分 /* 会匹配第一个 */,所以下面案例都是会报错的:

// 1: 块级注释嵌套
/*
注释 1
/* 注释 1.1 */
 */

// 2: 正则表达式字面量
/*
var rm_a = /a*/.match(s);
*/

字面量

字面量,又名直接量(Literals)

// 空字面量
null

// 布尔字面量
true
false

// 数值字面量
123_000
0b10101
0o63
0x4f
1.6e5

// 字符串字面量
'foo'
"bar"
`foo-bar`

// 对象字面量
{ a: 'foo', b: 'bar', c: 42 }

// 数组字面量
[1954, 1974, 1990, 2014]

// 正则表达式字面量
/ab+c/g
/(?:)/

标识符

标识符:给变量、函数、函数形参、属性(对象的键)命名的字符序列

命名规则(必须遵守的硬性规定):

其实标识符中的字母也可以包含扩展的 ASCII 或 [[#Unicode]] 字母字符,但不推荐

命名规范(建议遵守的软性要求):

关键字和保留字

JavaScript 保留关键字

JavaScript 保留了一些标识符为自己所用(已经用到或以后扩展使用)

abstract arguments boolean break byte
case catch char class const
continue debugger default delete do
double else enum eval export
extends false final finally float
for function goto if implements
import in instanceof int interface
let long native new null
package private protected public return
short static super switch synchronized
this throw throws transient true
try typeof var void volatile
while with yield

JavaScript 对象、属性和方法

JavaScript 内置的对象、属性和方法名

Array Date eval function hasOwnProperty
Infinity isFinite isNaN isPrototypeOf length
Math NaN name Number Object
prototype String toString undefined valueOf

Java 保留关键字

JavaScript 经常与 Java 一起使用

getClass java JavaArray javaClass JavaObject JavaPackage

window 保留关键字

浏览器运行时

alert all anchor anchors area
assign blur button checkbox clearInterval
clearTimeout clientInformation close closed confirm
constructor crypto decodeURI decodeURIComponent defaultStatus
document element elements embed embeds
encodeURI encodeURIComponent escape event fileUpload
focus form forms frame innerHeight
innerWidth layer layers link location
mimeTypes navigate navigator frames frameRate
hidden history image images offscreenBuffering
open opener option outerHeight outerWidth
packages pageXOffset pageYOffset parent parseFloat
parseInt password pkcs11 plugin prompt
propertyIsEnum radio reset screenX screenY
scroll secure select self setInterval
setTimeout status submit taint text
textarea top unescape untaint window

HTML 事件句柄

onblur onclick onerror onfocus
onkeydown onkeypress onkeyup onmouseover
onload onmouseup onmousedown onsubmit

Unicode

ECMAScript 要求支持 Unicode 字符集,Unicode 是 ASCII 和 Latin-1 的超集,支持地球上几乎所有在使用的语言

Unicode 转义序列

在有些计算机硬件和软件中,无法显示或输入 Unicode 字符全集,JavaScript 定义了一种特殊序列来代表任意 16 位 Unicode 内码。格式为 \u 其后跟随 4 个十六进制数,可以用于 JavaScript 字面量、正则表达式和标识符中(关键字除外)

const café = 1 // 使用 Unicode 字符定义一个变量
caf\u00e9      // => 1;使用转义序列访问这个变量
caf\u{e9}      // => 1;相同转义序列的另一种形式

JavaScript 早期版本只支持 4 位数字转义序列;带花括号的版本是 ES6 新增的,目的是更好地支持大于 16 位的 Unicode 码点,比如表情符号:

console.log('\u{1F600}') // 😀

Unicode 归一化

如果在程序中使用了非 ASCII 字符,那必须知道 Unicode 允许用多种编码方式表示同一个字符

如,字符串 'é'

这两种编码在文本编辑器中看起来完全相同,但它们的二进制编码不同,因此 JavaScript 认为它们不同,而这可能导致非常麻烦的问题:

const café = 1; // 'caf\u{e9}'
const café = 2; // 'cafe\u{301}'
café            // => 1
café            // => 2

分号

JavaScript 使用分号 ; 将语句分隔开,但并不严格要求分号

当有地方需要分号时,它会在背后悄悄地添加分号,这被称为自动分号插入(Automatic Semicolon Insertion)

自动分号插入(ASI)规则

在解析源代码的过程中,当发现这些特殊情况时,JavaScript 解析器会自动添加一个分号:

  1. 下一行是别的代码开始,打断了当前的代码
  2. 出现 } 时,会在其之前插入一个分号
  3. 到达源代码文件的结尾
  4. 当前行有 return 语句
  5. 当前行有 break 语句
  6. 当前行有 continue 语句
  7. 当前行有 throw 语句
  8. 当前行有 yieldyield* 语句

省略分号的注意点

一个新行如果以下面符号开头,需要在最前面手动加上分号:

实际代码中,这几种情况颇为少见