# ES6 let. More memory?

**URL:** <https://forum.kirupa.com/t/es6-let-more-memory/585338>\
**Category:** web dev\
**Created:** [March 19, 2015, 1:51pm UTC](https://forum.kirupa.com/t/es6-let-more-memory/585338 "2015-03-19T13:51:34Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![senocular](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/senocular/32/7217_2.png) [@senocular](https://forum.kirupa.com/u/senocular)\
**Post date:** [March 19, 2015, 1:51pm UTC](https://forum.kirupa.com/t/es6-let-more-memory/585338/1 "2015-03-19T13:51:34Z")

</div>

If let is creating new scopes for variables to live in, does that mean a greater memory footprint for executing code? Consider a loop containing a function which will capture each of the scopes for each loop…

```javascript
let fns = [];
for (let i = 0; i < 100000; i++) {
   fns.push(function(){
      // ...
   });
}
fns; // 100000 captured scopes?

```

If not using i in that function, would it be more efficient to be using var for the `i` variable thereby only allowing one scope to be captured (or none, since a new one, I presume, would not be created for the for block)?

---

<div class="post-metadata">

**Author:** ![krilnon](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/krilnon/32/34_2.png) [@krilnon](https://forum.kirupa.com/u/krilnon)\
**Post date:** [March 19, 2015, 2:55pm UTC](https://forum.kirupa.com/t/es6-let-more-memory/585338/2 "2015-03-19T14:55:01Z")

</div>

`let`s don’t create a scope; they just create variable bindings within block scopes ([lexical environments](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-execution-contexts)) rather than within function scopes ([variable environments](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-execution-contexts)), but both block and function scopes exist either way. (That may sound a bit picky, but scopes in non-`with` JavaScript are lexical scopes, which means they’re determined during the lexing phase of parsing, and don’t necessarily have any runtime representation.)

If you have a nested function, it will capture the chain of lexical and variable environments, which is sort of the same thing as saying that the resultant closure closes-over those `let`, `const`, and `var` bindings.

[`for(let ...)` loops create a new lexical environment, unlike `for(var ...)` loops](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-for-statement-runtime-semantics-labelledevaluation), so the nested function in your example would hypothetically capture a unique binding of `i` in each iteration of the loop, which leads to the memory concerns you cited. (This is actually fairly unique to the loop form. Normally, `let` itself doesn’t create a lexical environment, blocks do.)

> [@senocular](#):
>
> If not using i in that function, would it be more efficient to be using var for the i variable thereby only allowing one scope to be captured (or none, since a new one, I presume, would not be created for the for block)?

The efficiency is up to the implementation. I think the case of not even using a captured variable is pretty easy to statically detect, in the absence of `eval`, or running in a debug mode which tries to let you look at normally-invisible structures.

The spec is consistently insistent that things like lexical environments and execution contexts _don’t necessarily need to translate into actual, in-memory structures in an implementation_, so if an implementation can get away with unobservably not tracking certain state, that’s fair game.

> when the terms “LexicalEnvironment”, and “VariableEnvironment” are used without qualification they are in reference to those components of the running execution context.

> An execution context is purely a specification mechanism and need not correspond to any particular artefact of an ECMAScript implementation. **It is impossible for ECMAScript code to directly access or observe an execution context**.

So you could test what current ES6 implementations do by checking out memory usage differences between `let`/`const` and `var`, or by looking at the source code. My hunch is that they don’t optimize it yet, but will quickly when usage of ES6 ramps up.

* * *

There’s a relevant discussion on es-discuss here: [In ES6, do for loops with a let/const initializer create a separate scope?](https://esdiscuss.org/topic/in-es6-do-for-loops-with-a-let-const-initializer-create-a-separate-scope)

* * *

I currently get different output for this code in Chromium 43 and FF 36, so keep that in mind:

```javascript
void function(){ 
	'use strict'
	let fns = []
	for(let i = 0; i < 5; i++){ 
		fns.push(function(){ console.log(i) }) 
	}
	fns[3]()
}() // (3 in V8, 5 in SpiderMonkey)

```

I’m pretty sure that V8’s interpretation is the correct one according to the most recent ES6 spec draft I’ve seen.

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/senocular/32/7217_2.png) [@senocular](https://forum.kirupa.com/u/senocular)\
**Post date:** [March 19, 2015, 3:40pm UTC](https://forum.kirupa.com/t/es6-let-more-memory/585338/3 "2015-03-19T15:40:49Z")

</div>

Great information, thanks!

Something else I considered was that my particular use case may not be very useful anyway. I see very little reason to create separate function instances in a loop if those functions aren’t going to be capturing anything uniquely defined in that loop. The only exception I can think of being adding custom properties to the function object itself, for whatever reason you might have to do so.

> [@krilnon](#):
>
> I currently get different output for this code in Chromium 43 and FF 36

I noticed that before - yesterday, I think - when trying the same code. I agree that V8 is correct.

Something else I noticed what that you can shadow loop variables in the loop body in both ff and chrome, though babeljs throws an error. I’m pretty sure this is allowed so likely just a bug in babel.

```auto
!function(){"use strict"
for (let i = 0; i<3; i++){
  let i = 5;
  console.log(i); // three logs of: 5
}
}()

```

---

<div class="post-metadata">

**Author:** ![krilnon](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/krilnon/32/34_2.png) [@krilnon](https://forum.kirupa.com/u/krilnon)\
**Post date:** [March 19, 2015, 4:02pm UTC](https://forum.kirupa.com/t/es6-let-more-memory/585338/4 "2015-03-19T16:02:08Z")

</div>

> [@senocular](#):
>
> Something else I considered was that my particular use case may not be very useful anyway. I see very little reason to create separate function instances in a loop if those functions aren’t going to be capturing anything uniquely defined in that loop.

Well, you might want the convenience of using a lambda/fat-arrow or functional-style array method without hoisting that function instance outside the loop. Even with long-form functions, you might want to do the same thing but keep the semantics of `this` compared to fat arrow functions.

> [@senocular](#):
>
> Something else I noticed what that you can shadow loop variables in the loop body in both ff and chrome, though babeljs throws an error. I’m pretty sure this is allowed so likely just a bug in babel.

Yup that’s covered in the es-discuss link I cited. :trout: Unless the spec for that changed in the last 9 months.

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/senocular/32/7217_2.png) [@senocular](https://forum.kirupa.com/u/senocular)\
**Post date:** [March 19, 2015, 4:20pm UTC](https://forum.kirupa.com/t/es6-let-more-memory/585338/5 "2015-03-19T16:20:09Z")

</div>

> [@krilnon](#):
>
> Yup that’s covered in the es-discuss link I cited.

yeah I found that one earlier when looking into this too. Wasn’t sure if babel, which seems pretty popular right now, is stumbling or something else. I guess mentioning it was pretty redundant since the link pretty much explains this very behavior :trout: (then again I was referencing babel which was new 😉 )
