This post is part of my miniseries, Declaring Variables in JavaScript.
Declaring Variables in JavaScript
Daniel Brady ・ Jan 20 '20 ・ 2 min read
If you've already read some of the sibling posts, you can skip straight to here.
CONTENTS
- The basics: declaring variables
- The specifics: declaring variables in JavaScript
- What is it?
- Okay...but what does it do?
- What is it good for?
- When should I use something else?
- So when should I use it?
The basics: declaring variables
Let's begin at the beginning: variable declarations declare variables. This may seem obvious to many, but in practice we often confuse variables with values, and it is important, particularly for this conversation, that we are clear on the differences.
A variable is a binding between a name and a value. It's just a box, not the contents of the box, and the contents of the box may vary either in part or in whole (hence the term 'variable').
The kind of box you use, that is, the declarator you use to create a binding, defines the way it can be handled by your program. And so when it comes to the question of, "How should I declare my variables?" you can think of the answer in terms of finding a box for your data that is best suited to the way you need to manipulate it.
The specifics: declaring variables in JavaScript
At the time of this writing, JavaScript gives us these tools for declaring our variables:
var
let
const
💡 The
function
keyword is not, in fact, a variable declarator, but it can create a bound identifier. I won't focus on it here; it's a bit special because it can only bind identifiers to values of a particular type (function objects), and so has a rather undisputed usage.
Why so many options? Well, the simple answer is that in the beginning, there was only var
; but languages evolve, churn happens, and features come (but rarely go).
In this post, we'll dive into the behavior of var
: the original variable declarator.
What is it?
var
is a universally supported construct of JavaScript; it has been since the beginning and will be until the end. It's as old as the language itself, and very powerful.
A
var
statement declares variables that are scoped to the running execution context's VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized toundefined
when created. Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collectively define only one variable. A variable defined by a VariableDeclaration with an Initializer is assigned the value of its Initializer's AssignmentExpression when the VariableDeclaration is executed, not when the variable is created.
Source: ECMAScript 2019 Language Specification, §13.3.2
Okay...but what does it do?
Translation? 🤨 Let's learn by doing.
var
does what it says on the tin: it names a variable and lets me use it.
During compilation, that variable is
- scoped to the nearest enclosing function (or the global scope if we're not in one)
-
created and initialized to
undefined
during the instantiation of that scope, and - available for reference anywhere within the scope at run-time
💡 You might come across the term "variable hoisting" in your JS travels: it refers to this 'lifting' behavior of the JS compiler with respect to creating variables during instantiation of the scope itself. All declarators hoist their variables, but the accessibility of those variables varies with the tool you use.
At run-time, references to my variable are evaluated and manipulated.
⚠️ In JavaScript error messages, "not defined" is not the same thing as having a value of
undefined
😓 The term "not defined," in this context, should really be "not declared."
If I combined my var
declaration with a value assignment, that value doesn't go into the box until the assignment is evaluated, and evaluation happens at run-time.
Furthermore, additional declarations of the same name in the same scope using var
have no effect: it's always the same variable.
What is it good for?
var
, like let
and const
, gives the ability to encapsulate, manipulate, share, and hide data in boxes within my JavaScript.
But unlike let
and const
, my box is accessible anywhere within the nearest enclosing function, not merely the closest lexical environment, and so var
really shines at function-level state management.
The ability of var
to transcend the lexical environment of blocks and let me add to the state of the nearest enclosing function is particularly powerful, if perhaps an uncommon usage.
And since functions inherit the environment of their parents thanks to closure, nested functions can access the var
s of their parents, but parents cannot assert their authority as parents and come into their children's bedrooms to mess with their var
s.
When should I use something else?
Sometimes, I only need a box to hold some data for a short while, not an entire function. Since var
scopes my data to the nearest enclosing function, it communicates "widely used" to the reader and so it's not the best tool for this job. In this situation, let
is better.
Sometimes, I want a box that only holds one thing throughout my program, and/or I want your readers to know I don't intend to make changes to the data I put in it. Since var
makes boxes that are always open to having their contents replaced, it communicates the wrong thing and so it's not the best tool for this job. In this situation, const
is better.
Using var
inappropriately can hurt the readability and maintainability of my code because I'm communicating the wrong thing and not encapsulating my data as well as I could be.
To learn how to communicate better in my code, I dove into the other tools available and wrote about what I found:
So when should I use it?
I prefer var
for holding values whose names will be referenced throughout most, or all, of the current function.
If, during the course of development, it makes sense to reduce the scope and/or accessibility of my data, I can move my declaration to a new scope and swap out var
for a tool like let
, const
, or function
that lets me do so.
With var
, like let
, I am free to replace the contents of my box with something different or new at any time I might need, which makes it a great choice for tracking changes over time in situations where an immutable approach to managing function-level state is not practical to implement.
Every tool has its use. Some can make your code clearer to humans or clearer to machines, and some can strike a bit of balance between both.
"Good enough to work" should not be "good enough for you." Hold yourself to a higher standard: learn a little about a lot and a lot about a little, so that when the time comes to do something, you've got a fair idea of how to do it well.
Oldest comments (0)