关于javascript:为什么不要比var慢?

Why is let not slower than var?

综上所述,varlet的区别在于它们在一定范围内的生命。

因此,如果我们从这个答案中举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(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
}());

  • i(与var一起声明)生活在整个function内。
  • j(与let一起声明)只在for循环内生存。

对我来说,这意味着在每次迭代之后,javascript除了声明和分配一个变量之外,在let的情况下,它还需要执行一个额外的步骤:清理j

但是如果我正确地阅读规范,还有很多:

  • 对于var的情况,执行以下步骤:
  • IterationStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement

  • If the first Expression is present, then

    • Let exprRef be the result of evaluating the first Expression.
    • Let exprValue be GetValue(exprRef).
    • ReturnIfAbrupt(exprValue).
  • Return ForBodyEvaluation(the second Expression, the third Expression, Statement, ? ?, labelSet).
  • 但在let的例子中,执行了12个步骤的庞大列表,包括创建新的声明性环境。


    IterationStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement

  • Let oldEnv be the running execution context’s LexicalEnvironment.
  • Let loopEnv be NewDeclarativeEnvironment(oldEnv).
  • Let isConst be the result of performing IsConstantDeclaration of > 1. LexicalDeclaration.
  • Let boundNames be the BoundNames of LexicalDeclaration.
  • For each element dn of boundNames do

    • If isConst is true, then

      • Perform loopEnv.CreateImmutableBinding(dn, true).
    • Else,

      • Perform loopEnv.CreateMutableBinding(dn, false).
      • Assert: The above call to CreateMutableBinding will never return an abrupt completion.
  • Set the running execution context’s LexicalEnvironment to loopEnv.
  • Let forDcl be the result of evaluating LexicalDeclaration.
  • If forDcl is an abrupt completion, then

    • Set the running execution context’s LexicalEnvironment to oldEnv.
    • Return Completion(forDcl).
  • If isConst is false, let perIterationLets be boundNames otherwise let perIterationLets be ? ?.
  • Let bodyResult be ForBodyEvaluation(the first Expression, the second Expression, Statement, perIterationLets, labelSet).
  • Set the running execution context’s LexicalEnvironment to oldEnv.
  • Return Completion(bodyResult).
  • 那么,为什么在运行下面的测试(我从前面提到的同一个问题中得出)时,var的运行速度比let慢,而不是像我预期的那样相反?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    (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({});

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    TEST 1
    var: 147.500ms
    let: 138.200ms

    TEST 2
    var: 141.600ms
    let: 127.100ms

    TEST 3
    var: 147.600ms
    let: 122.200ms


    我同意@derek會會會會會會會會會會會會

    但是,如果是var,您可能会错过一些步骤:

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

    IterationStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement

  • If the first Expression is present, then

    • Let exprRef be the result of evaluating the first Expression.
    • Let exprValue be GetValue(exprRef).
      • ReturnIfAbrupt(V).
      • If Type(V) is not Reference, return V.
      • Let base be GetBase(V).
      • If IsUnresolvableReference(V), throw a ReferenceError exception.
      • If IsPropertyReference(V), then
        • If HasPrimitiveBase(V) is true, then
          • Assert: In this case, base will never be null or undefined.
          • Let base be ToObject(base).
        • Return base.[[Get]](GetReferencedName(V), GetThisValue(V)).
      • Else base must be an Environment Record,
        • Return base.GetBindingValue(GetReferencedName(V), IsStrictReference(V)) (see 8.1.1).
    • ReturnIfAbrupt(exprValue).
  • Return ForBodyEvaluation(the second Expression, the third Expression, Statement, ? ?, labelSet).
  • 稍微额外的处理可能来自getValue。