为什么不慢于var?

Ade*_*lin 9 javascript performance var let

为了使一个极端的总结,之间的差异var,并let是一个范围之内他们的生活.

所以如果我们要从这个答案中得到这样的例子:

(function() {
  for (var i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(`i: ${i}`);
    }, i * 100);
  }
  // 5, 5, 5, 5, 5


  for (let j = 0; j < 5; j++) {
    setTimeout(function() {
      console.log(`j: ${j}`);
    }, 1000 + j * 100);
  }
  // 0, 1, 2, 3, 4
}());
Run Code Online (Sandbox Code Playgroud)

  • i(声明var)生活在整个function
  • j(声明着let)仅存在于for循环内.

对我来说,这意味着javascript,在每次迭代之后,除了声明和赋值给变量之外,let还需要执行额外的步骤:清理j

但如果我正在阅读规范,那么还有更多:

  1. 在这种情况下var,执行以下步骤:

IterationStatement:for(Expressionopt; Expressionopt; Expressionopt)语句

  1. 如果存在第一个表达式,那么
    • 设exprRef是评估第一个Expression的结果.
    • 设exprValue为GetValue(exprRef).
    • ReturnIfAbrupt(exprValue).
  2. 返回ForBodyEvaluation(第二个Expression,第三个Expression,Statement,«»,labelSet).
  1. 但在这种情况下let,执行了大量的12个步骤,包括新声明环境的创建.

    IterationStatement:for(LexicalDeclaration Expressionopt; Expressionopt)语句

    1. 让oldEnv成为正在运行的执行上下文的LexicalEnvironment.
    2. 设loopEnv为NewDeclarativeEnvironment(oldEnv).
    3. 让isConst成为执行IsConstantDeclaration> 1. LexicalDeclaration的结果.
    4. 让boundNames成为LexicalDeclaration的BoundNames.
    5. 对于boundNames的每个元素dn
      • 如果isConst为真,那么
        • 执行loopEnv.CreateImmutableBinding(dn,true).
      • 其他,
        • 执行loopEnv.CreateMutableBinding(dn,false).
        • 断言:上面对CreateMutableBinding的调用永远不会返回突然完成.
    6. 将正在运行的执行上下文的LexicalEnvironment设置为loopEnv.
    7. 让forDcl成为评估LexicalDeclaration的结果.
    8. 如果forDcl突然完成,那么
      • 将正在运行的执行上下文的LexicalEnvironment设置为oldEnv.
      • 返回完成(forDcl).
    9. 如果isConst为false,则让perIterationLets为boundNames,否则perIterationLets为«».
    10. 让bodyResult成为ForBodyEvaluation(第一个Expression,第二个Expression,Statement,perIterationLets,labelSet).
    11. 将正在运行的执行上下文的LexicalEnvironment设置为oldEnv.
    12. 返回完成(bodyResult).

那么为什么在运行以下测试时(我从之前引用的同一个问题中得到),var执行速度比let我预期的要慢,反之亦然?

(function() {
  let varTime = performance.now()
  for (var i = 0; i < 100000000; i++) {}
  varTime = performance.now() - varTime;
  console.log('var', varTime)


  let letTime = performance.now()
  for (let i = 0; i < 100000000; i++) {}
  letTime = performance.now() - letTime;
  console.log('let', letTime)
}).call({});
Run Code Online (Sandbox Code Playgroud)

TEST 1
var: 147.500ms
let: 138.200ms

TEST 2
var: 141.600ms
let: 127.100ms

TEST 3
var: 147.600ms
let: 122.200ms
Run Code Online (Sandbox Code Playgroud)

Jee*_*Mok 1

我同意@Derek \xe6\x9c\x95\xe6\x9c\x83\xe5\x8a\x9f\xe5\xa4\xab 的观点,如果不深入研究实现,真的很难推理。

\n\n

但是,如果出现以下情况,您可能会错过一些步骤var

\n\n

在 的情况下var,实际上执行了更多步骤:

\n\n
\n

迭代语句:for (Expressionopt;Expressionopt;Expressionopt)语句

\n\n
    \n
  1. 如果第一个表达式存在,则\n \n
      \n
    • 令 exprRef 为计算第一个表达式的结果。
    • \n
    • 令 exprValue 为GetValue (exprRef)。
    • \n
      • \n
      • 如果突然返回(V)。
      • \n
    • \n
      • \n
      • 如果 Type(V) 不是 Reference,则返回 V。
      • \n
    • \n
      • \n
      • 令基数为 GetBase(V)。
      • \n
    • \n
      • \n
      • 如果 IsUnresolvableReference(V),则抛出 ReferenceError 异常。
      • \n
    • \n
      • \n
      • 如果 IsPropertyReference(V),则
      • \n
    • \n
      • \n
        • \n
        • 如果 HasPrimitiveBase(V) 为 true,则
        • \n
      • \n
    • \n
      • \n
        • \n
          • \n
          • 断言:在这种情况下,base 永远不会为 null 或未定义。
          • \n
        • \n
      • \n
    • \n
      • \n
        • \n
          • \n
          • 令base 为ToObject(base)。
          • \n
        • \n
      • \n
    • \n
      • \n
        • \n
        • 返回基数。[[Get]](GetReferencedName(V), GetThisValue(V))。
        • \n
      • \n
    • \n
      • \n
      • 否则基础必须是环境记录,
      • \n
    • \n
      • \n
        • \n
        • 返回base.GetBindingValue(GetReferencedName(V), IsStrictReference(V))(参见8.1.1)。
        • \n
      • \n
    • \n
    • ReturnIfAbrupt(exprValue)。
    • \n
  2. \n
  3. 返回ForBodyEvaluation(第二个表达式,第三个表达式,语句,\xc2\xab\xc2\xbb,labelSet)。
  4. \n
\n
\n\n

稍微额外的处理可能来自GetValue

\n