DEV Community

Cover image for Using JS: var
Daniel Brady
Daniel Brady

Posted on • Updated on

Using JS: var

This post is part of my miniseries, Declaring Variables in JavaScript.

If you've already read some of the sibling posts, you can skip straight to here.


CONTENTS

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?

Quick! To the world wide web

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 to undefined 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.

Var introduces a variable

During compilation, that variable is

  1. scoped to the nearest enclosing function (or the global scope if we're not in one)
  2. created and initialized to undefined during the instantiation of that scope, and
  3. 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.

Var variables are scoped to functions and initialized to undefined by the compiler

⚠️ 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.

Var assignments happen in order 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.

Re-declarations with var do nothing

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 vars of their parents, but parents cannot assert their authority as parents and come into their children's bedrooms to mess with their vars.

Showcasing the behavior of var with respect to nested functions

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.

Using var for loop variables exposes them outside the loop

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.

Const is better than var for bindings that should never change

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)