Photo by artM from FreeImages
When learning a new language, it makes sense that some parts will seem kinda familiar while others are quite mysterious. I'm studying JavaScript now and there are a few tricky bits I'd like to review in more detail.
var, let, const ◆ hoisting ◆ scope ◆ closure
Before we go into the topics above, let's take a look into how JavaScript is working 'behind the scenes'. The JavaScript Engine actually makes two different passes over your code and in the first pass, called the Creation phase, the code is not executed line by line. Instead, a global object and an associated this
are created, and variables get set up in memory. During the second pass is where the work begins, called the Execution phase because this is when your code is actually run line-by-line and values are set to the variables previously defined and stored in memory during the first pass.
Having a good understanding of how the JavaScript engine actually evaluates your code is a valuable tool. This will be more evident as we explore the topics mentioned inside the box at the top of this post.
Prior to the release of ES6, var
was the only choice for variable declaration. As it turns out, var
can cause some trouble in your code due to its flexible nature, and that is where let
and const
come to the rescue. Let's take a look at each variable declaration type a bit more closely.
Before we go over var
, let
, and const
, it is important to review the concept of scope. Scope is defined as "the current context of execution" and is basically where a variable is available to use. In the case of var
, it is function scoped when declared within a function and global scoped when declared outside a function. Function scoped means that the variable is accessible and available within that function only. This is quite different from global scoping where the variable becomes available to the entire window.
Consider this example:
var mood = 'happy';
function changeMood() {
var newMood = 'curious';
console.log(newMood);
}
console.log(mood);
changeMood();
Here, the first variable declaration is within the global scope, so it is available for use anywhere within the window. When you console.log(mood)
, the output is 'happy', accessed from the global scope.
The second variable declaration, inside the changeMood()
function, exhibits functional scope behavior. When changeMood()
is called, the output is 'curious' since the variable newMood
was declared within the function. It is available to the function itself, but if you tried to console.log(newMood)
from outside of the function, you get: ReferenceError: newMood is not defined
. The scope of newMood
is only within the changeMood()
function.
In the case of let
and const
, variable declarations using these keywords are block scoped only. This means that variable declarations can only be accessed within the block (inside the curly brackets only) from which they were declared. Keeping variable declarations out of the global scope helps to prevent bugs and issues with overwriting variables previously declared.
Now that we've briefly covered scope, let's take a look at some of the behaviors of var
that led to the need for let
and const
. When you use var
to declare a variable, JavaScript won't prevent you from redeclaring that variable, in fact it won't even let you know that you have overwritten a previously declared variable. Oh no!
var cat = 'Coco';
var cat = 'Beebee';
console.log(cat); //=> Beebee
The output for cat
is 'Beebee' since var
allows for redeclaration of variables. This is an easy way to put bugs into your program and should be avoided. Take a look at how let
and const
prevent this down below:
let cat = 'Coco'; const cat = 'Coco';
let cat = 'Beebee'; const cat = 'Beebee';
console.log(cat); console.log(cat);
`SyntaxError: Identifier 'cat' has already been declared`
Also note that var
and let
variables can be updated, whereas const
can only be defined once and may not be updated.
Remember how the JavaScript engine processes your code in two passes? Good, let's put that knowledge to use. Hoisting is defined as when variables and function declarations are 'moved' to the top of scope before execution. Well, they aren't really physically moved, but during the Creation phase they are set into memory and variables declared with var
are initialized with a value of undefined
. Variables declared with let
and const
are not initialized with a value, but are still set into memory. So if you try to use a variable with let
or const
before it is declared, a Reference Error
occurs. Let's take a look at some examples:
console.log (greeting);
var greeting = "hola"
Will be interpreted like this:
var greeting;
console.log (greeting); //=> greeting is undefined
greeting = "hola"
And when using let
or const
:
console.log (greeting); console.log (greeting);
let greeting = "hola"; const greeting = "hola";
ReferenceError: Cannot access 'greeting' before initialization
An interesting note about const
:
Variables declared with const
must be initialized at the time of declaration.
const greeting;
console.log (greeting);
greeting = "hola";
SyntaxError: Missing initializer in const declaration
So in summary:
Scope | Update? | Re-declare? | Hoisted? | Initialized | |
---|---|---|---|---|---|
var | global or functional | yes | yes | yes | undefined |
let | block | yes | no | yes | no |
const | block | no | no | yes | required |
The last topic I would like to cover here is closure. Closure is when an inner function is able to access the outer function's variables, creating a scope chain. This should make sense now that we understand scope and hoisting. The following example should be helpful:
let learn = 'learning experience! ';
let result;
function phrase(a){
return function(b) {
return (a + b + learn)
}
}
result = phrase('Enjoy ')('your ');
console.log(result); //=> Enjoy your learning experience!
The innermost return of this function uses variables that have been declared outside of the inner function. This is possible because the scope chain is maintained by the inner function. When the function is executed, this scope chain allows access to the variables that live inside the outer function. This example would also work with var
(but don't!) and will not work with const
due to the need to initialize variables using const
at the time of declaration.
Hopefully this helps to clarify some of the more mysterious parts of JavaScript functionality. Understanding these tricky parts will help you write better code!
Top comments (0)