Cover image by Christiaan Colen on Flickr
With ES2015 JavaScript got a bunch of new features, two of them being let
and const
keywords that let you declare your local variables.
var
Variables you declare with var
will be scoped to the function they were declared in.
This means, even if you declare them in nested blocks (other curly braces) inside of a function, they will still be scoped to the function.
For example, if you declare it inside of a try block like this:
function f(x) {
try {
var y = x.toString();
} catch(e) {
return "";
}
return y;
}
It will actually be declared like that:
function f(x) {
var y;
try {
y = x.toString();
} catch(e) {
return "";
}
return y;
}
Every var
declaration will be hoisted to the top of the function. This is why it is considered a best practice
to declare var
s at the top of a function, it will end up there anyway.
If you want to scope a var
to a code block, you would have to fill or replace it with a function expression and call the function right after definition.
function f(x) {
var y;
if (x >= 0) (function() {
var z = Math.random() * 10;
y = x * z;
})();
return y;
}
The anonymous function has access to f()
s local variables, so it can use y
, but z
is only defined inside of the anoynmous function and can't be accessed inside f()
.
As you can see, this is rather sub-optimal, but this was the way for many years. If you are bound to use ES5 and for some reason can't use a compiler like Babel, you still have to do this.
let
The let
keyword is now a way to declare variables that aren't scoped to a function, but to a block. This means any code enclosed by curly braces confines that variable.
function f(x) {
let y;
if (x >= 0){
let z = Math.random() * 10;
y = x * z;
} else {
y = 10;
}
return y;
}
In this example z
is only accessible inside the if
-branch, the else
branch or the rest of f()
could not access it.
Blocks can alse be used without any statement.
function f(x) {
let results = [];
// first calculations
{
let a = 10 * 10 * x;
let b = a * Math.PI;
results.push(b);
}
// second calculations
{
let a = 3 + 2 + x;
let b = a * a;
let c = a + b;
results.push(c);
}
return results;
}
This allows to structure code and scope variables to the part of the function they are used. As you can see, the blocks have access to the outer scope variables results
and x
.
const
So what is the const
keyword about? Well, you may have seen it in other languages like C or Java. In JavaScript it declares a variable that can't be changed, but be careful this only applies to the direct content of that variable. Otherwise it scopes as let
.
This won't work:
const a = 10;
a = 11;
These on the other hand will:
const a = [1, 2, 3];
a.push(4);
const b = {x: 10, y: 20};
b.z = 30;
To prevent objects (arrays are objects too) from being changed, you need to freeze
them manually
, which impacts performance.
This will throw an error:
const a = Object.freeze([1, 2, 3]);
a.push(4);
When Use What?!
I try to use const
as much as possible, if functions get bigger, it often gets cumbersome to find out how often and where a variable was changed on the way down.
Sometimes it helps to use let
with try/catch
, because I have to set a variable inside try
and it needs to be accessible after the try
.
I avoid var
completely nowadays. const
and let
have the same scoping, their only difference is their write-access, so it's easy to reason about them interchangably. var
on the other hand works much different from them and I think it just makes stuff more complicated when mixed.