在 JavaScript AST 中遇到 EmptyStatement 的所有方式有哪些?

What are all the ways you can encounter an EmptyStatement in a JavaScript AST?

我正在写一个JS转译器,想知道我会遇到什么。除了晦涩难懂的 EmptyStatement 之外,我几乎处理了所有边缘情况。我遇到的一个地方是这里:

for (arrL = arr.length; arrL--; arr[arrL] *= baseIn);

AST 看起来像这样:

{
  "type": "Program",
  "body": [
    {
      "type": "BlockStatement",
      "body": [
        {
          "type": "AssignmentExpression",
          "left": {
            "type": "Identifier",
            "start": 4014,
            "end": 4018,
            "name": "arrL"
          },
          "right": {
            "type": "MemberExpression",
            "object": {
              "type": "Identifier",
              "start": 4021,
              "end": 4024,
              "name": "arr"
            },
            "property": {
              "type": "Identifier",
              "start": 4025,
              "end": 4031,
              "name": "length"
            },
            "computed": false
          },
          "operator": "="
        },
        {
          "type": "WhileStatement",
          "test": {
            "type": "Literal",
            "value": true,
            "raw": "true"
          },
          "body": {
            "type": "BlockStatement",
            "body": [
              {
                "type": "IfStatement",
                "test": {
                  "type": "UpdateExpression",
                  "argument": {
                    "type": "Identifier",
                    "start": 4033,
                    "end": 4037,
                    "name": "arrL"
                  },
                  "operator": "--",
                  "prefix": false
                },
                "consequent": {
                  "type": "BlockStatement",
                  "body": [
                    {
                      "type": "EmptyStatement",
                      "start": 4061,
                      "end": 4062
                    },
                    {
                      "type": "AssignmentExpression",
                      "left": {
                        "type": "MemberExpression",
                        "object": {
                          "type": "Identifier",
                          "start": 4041,
                          "end": 4044,
                          "name": "arr"
                        },
                        "property": {
                          "type": "Identifier",
                          "start": 4045,
                          "end": 4049,
                          "name": "arrL"
                        },
                        "computed": true
                      },
                      "right": {
                        "type": "Identifier",
                        "start": 4054,
                        "end": 4060,
                        "name": "baseIn"
                      },
                      "operator": "*="
                    }
                  ]
                },
                "alternate": {
                  "type": "BlockStatement",
                  "body": [
                    {
                      "type": "BreakStatement"
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  ]
}

我什至不知道 EmptyStatement 指的是什么哈哈。你 运行 变成 EmptyStatement 的其他情况是什么?

如果您仔细查看 for 语句,您会发现它的正文仅包含 ;。那是一句空话。你会得到同样的效果,比如说,

if (a--);

尽管许多风格指南不鼓励这样做。 {} 更清楚了,恕我直言。那是一个空块,而不是一个空语句。 {;} 将是一个仅由空语句组成的块,这也很少见,但您可能会发现类似的内容:

if (debugging) {
    /* Commented out for now */ ;
}

那么,什么是空语句?这是一个只有分号终止符的语句。空的,正如它所说。

有趣的是,for 语句已经被脱糖,这可能会造成混淆。但这是正确的; for(init; test; advance) body; 在语义上等同于

{
  init;
  while(true) {
    if (test) {
      body;
      advance;
    }
    else 
      break;
  }
}

显然,即使它对 for 语句进行了脱糖处理,它仍保留了空主体。这可能是为了有一个地方来挂起行号,或者可能只是为了更容易将其留在解析中的那个位置。将(隐式)while (test) { ... } 奇怪地转换为 while (true) { if (test) { ... } else break; } 可能是为了简化基本块的分解,或者启用重新排序代码的标准优化。不过只是猜测。