阿西河

所有教程

公众号
🌙
阿西河前端的公众号

我的收藏

    最近访问  (文章)

      教程列表

      抓包专区
      测试专区

      JS 语法总结

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

      格式控制符

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

      使用 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() 的调用始终无效。这种方式可以屏蔽任意多行的代码,也可以屏蔽一行代码的一部分。

      Hashbang 注释

      专用的第三注释语法,即 hashbang 注释,正在用 ECMAScript 进行标准化

      请参阅 Hashbang 语法建议:https://github.com/tc39/proposal-hashbang

      哈希注释的行为与单行注释(//)完全相同,但它以#开头! 并且仅在脚本或模块的绝对开始时有效。

      还要注意,在 #! 之前不允许使用任何形式的空格。 注释包含#之后的所有字符! 直到第一行的结尾; 仅允许这样的评论之一。

      hashbang 注释指定要用于执行脚本的特定 JavaScript 解释器的路径。 示例如下:

      #!/usr/bin/env node
      
      console.log("Hello world");
      

      JavaScript中的Hashbang注释模仿Unix中的shebang,该shebang用于通过适当的解释器运行文件。

      尽管hashbang注释之前的BOM在浏览器中可以使用,但不建议在hasbang脚本中使用BOM。 当您尝试在Unix / Linux中运行脚本时,BOM将不起作用。 因此,如果要直接从Shell运行脚本,请使用不带BOM的UTF-8。

      关键字

      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 };
      

      https://a.axihe.com/img/api/jses/Code_mQq5KaTbnG.png

      数组直接量

      更多信息可以参考 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;
      
      1. 当捕获到标识符输入流的结尾,并且无法将单个输入流转换为一个完整的程序时,将在结尾插入一个分号。

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

      a = b
      ++c
      
      // 将被 ASI 转换为
      
      a = b;
      ++c;
      
      1. 当语句中包含语法中的限制产品后跟一个行终止符的时候,将会在结尾插入一个分号。带“这里没有行终止符”规则的语句有:
      • 后置运算符(++ 和 –)
      • continue
      • break
      • return
      • yield、yield*
      • module
      return
      a + b
      
      // 将被 ASI 转换为
      
      return;
      a + b;
      
      目录
      目录