9

To make an extreme sum up, the difference between var and let is their life within a scope.

So if we are to take the example from this answer:

(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 (declared with var) lives within the entire function
  • j (declared with let) lives only within the for loop.

To me, this means that javascript, after each iteration, besides declaring and assigning to a variable, in the case of let it also needs to perform an extra step: clean up j

But if I'm reading the specs right, there's so much more:

  1. In the case of var, these steps are performed:

IterationStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement

  1. If the first Expression is present, then
    • Let exprRef be the result of evaluating the first Expression.
    • Let exprValue be GetValue(exprRef).
    • ReturnIfAbrupt(exprValue).
  2. Return ForBodyEvaluation(the second Expression, the third Expression, Statement, « », labelSet).
  1. But in the case of let, a whopping list of 12 steps are performed, including creations of new declarative environments.

    IterationStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement

    1. Let oldEnv be the running execution context’s LexicalEnvironment.
    2. Let loopEnv be NewDeclarativeEnvironment(oldEnv).
    3. Let isConst be the result of performing IsConstantDeclaration of > 1. LexicalDeclaration.
    4. Let boundNames be the BoundNames of LexicalDeclaration.
    5. 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.
    6. Set the running execution context’s LexicalEnvironment to loopEnv.
    7. Let forDcl be the result of evaluating LexicalDeclaration.
    8. If forDcl is an abrupt completion, then
      • Set the running execution context’s LexicalEnvironment to oldEnv.
      • Return Completion(forDcl).
    9. If isConst is false, let perIterationLets be boundNames otherwise let perIterationLets be « ».
    10. Let bodyResult be ForBodyEvaluation(the first Expression, the second Expression, Statement, perIterationLets, labelSet).
    11. Set the running execution context’s LexicalEnvironment to oldEnv.
    12. Return Completion(bodyResult).

So why is it that when running the following test (which I took from the same question I previously referenced) , var performs slower than let, and not vice versa, as I'm expecting?

(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({});
TEST 1
var: 147.500ms
let: 138.200ms

TEST 2
var: 141.600ms
let: 127.100ms

TEST 3
var: 147.600ms
let: 122.200ms
Adelin
  • 7,809
  • 5
  • 37
  • 65
  • 1
    it's not slower, because it's faster - the "internal workings" of the javascript engine are irrelevant – Jaromanda X Jul 26 '18 at 05:37
  • Your snippet looks inconclusive for me on Chrome. They both perform at similar speeds, either may be faster than the other, moderate margin of error – CertainPerformance Jul 26 '18 at 05:38
  • 1
    Possible duplicate of [Is there a performance difference between 'let' and 'var' in JavaScript](https://stackoverflow.com/questions/21467642/is-there-a-performance-difference-between-let-and-var-in-javascript) – NullPointer Jul 26 '18 at 05:39
  • @CertainPerformance out of 5 runs indeed `let` performs slower **in one** run – Adelin Jul 26 '18 at 05:39
  • 1
    The question is not a duplicate. I'm not asking if there is a performance difference, I know there is one. I'm asking why – Adelin Jul 26 '18 at 05:40
  • 3
    Just ran 10, `let` was slower in 7/10 tests. There's almost certainly a performance difference because they're not doing the exact same thing, but which way it goes does not yet seem conclusive based on what's seen here. (you might use `performance.now` rather than console time) – CertainPerformance Jul 26 '18 at 05:42
  • @CertainPerformance I switched to `performance.now` and see the same results as outlined in the question. What OS & browser & browser version are you using? – Adelin Jul 26 '18 at 05:47
  • 1
    Implementation details are hard to reason about if we don’t dive deep into the actual implementation. – Derek 朕會功夫 Jul 26 '18 at 06:15

1 Answers1

1

I agreed with @Derek 朕會功夫 that it is really hard to reason without diving down into the implementation.

But, you might have missed the some of the steps in case of var:

In the case of var, there are actually more steps performed:

IterationStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement

  1. 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).
  2. Return ForBodyEvaluation(the second Expression, the third Expression, Statement, « », labelSet).

The slightly additional processing could be from GetValue.

Jee Mok
  • 6,157
  • 8
  • 47
  • 80