Variable declaration and hoisting

Javascript Baseline pt. 1

Artem Dracontis
Feb 4, 2019 · 4 min read
Image for post
Image for post
Photo by Joshua Aragon on Unsplash

Recently I’ve dived into ECMA-262 (9th Edition / June 2018) specification and in this article I will try to explain variable hoisting using and . This article mainly summarise answers to several StackOverflow questions and my understanding of the specification.

The difference between and declarations is scoping. is scoped to the nearest function block and is scoped to the nearest enclosing block, which can be smaller than a function block. Both are global if outside any block. Also, variables declared with are not accessible before they are declared in their enclosing block.

Let’s review following interview task:

var x = function() {
return a;
var a = 1;
function a() { return 'hello'}
}

To answer what value will be returned, we should understood and concepts from the ECMA-262 specification.

A lexical environment consists of an environment record, which can be thought of as an object whose properties are the variable and function names declared within the associated execution context. It also has, for functions, identifiers from the formal parameter list in the function declaration or expression (e.g. effectively declares and as variables on foo's environment record).

A variable environment is just the part of a lexical environment within the execution context, essentially just the variables and functions declared within the current context.

Now we could easily rewrite code in terms of Environments using knowledge that we earned from the specification:

LexicalEnvironment:
outer: VariableEnvironment
VariableEnvironment:
a = function () { return 'hello' }
outer: global

So when we enter function we could initialise with as there is declaration, but we haven’t reach it yet, or with function definition. And of course functions definition will win over .

Let’s look at another interview question:

for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i + i + '' + i)
}, i*10)
}

I was expected output:

00
21
42
63
84

But got:

105
105
105
105
105

You may already know that Javascript is single-threaded language. So it will finish loop as it first in execution context and only then will switch to tasks created by method. So we could expect something like this from original code:

LexicalEnvironment:
outer:
LexicalEnvironment:
outer: VariableEnvironment
LexicalEnvironment:
outer: VariableEnvironment
LexicalEnvironment:
outer: VariableEnvironment
LexicalEnvironment:
outer: VariableEnvironment
LexicalEnvironment:
outer: VariableEnvironment
VariableEnvironment:
i = 5
outer: global

When loop finished we started tasks with the value of , that’s why we have same result in every statement.

This code was given with remark that I should use closure to make it work. Wikipedia gives plain and simple definition:

A closure is a record storing a function together with an environment.

To make it even simpler, closure is javascript function that have access to its own variables and all other variables declared outside in parent function definition or even globally.

So I’ve rewrite it with closures in mind:

for (var i = 0; i < 5; i++) {
let closure = (j) => () => console.log(j + j + '' + j)
setTimeout(closure(i), i*10)
}

It gives us the result as we wanted:

LexicalEnvironment:
outer:
LexicalEnvironment:
j = 0
outer: VariableEnvironment
LexicalEnvironment:
j = 1
outer: VariableEnvironment
LexicalEnvironment:
j = 2
outer: VariableEnvironment
LexicalEnvironment:
j = 3
outer: VariableEnvironment
LexicalEnvironment:
j = 5
outer: VariableEnvironment
VariableEnvironment:
i = 5
outer: global

But is there any simple solution? There is! Just change to :

for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i + i + '' + i)
}, i*10)
}

And in terms of Environments it will look like:

LexicalEnvironment:
outer:
LexicalEnvironment:
i = 0
outer: global
LexicalEnvironment:
i = 1
outer: global
LexicalEnvironment:
i = 2
outer: global
LexicalEnvironment:
i = 3
outer: global
LexicalEnvironment:
i = 4
outer: global

Understanding difference between and will help you to write better code with less complexity. Understanding Environments will help you to crack almost every question about hoisting and closures, because if you will be able to decompose every code blocks in terms of Environments you’ll see the right answer from it.

Hope this article and question links in it help you to get better understanding in this complex yet essential Javascript concepts. Feel free to left comment or private response if you have any remarks about the article and clap (you could left up to 50 claps!).

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store