Variables

Variables are a data structure that assigns a representative name to a value. They can contain data of any kind.

A variable's name is called an identifier. A valid identifier must follow these rules:

  • Identifiers can contain Unicode letters, dollar signs ($), underscore characters (_), digits (0-9), and even some Unicode characters.
  • Identifiers can't contain whitespace, because the parser uses whitespace to separate input elements. For example, if you try to call a variable my Variable instead of myVariable, the parser sees two identifiers, my and Variable, and throws a syntax error ("unexpected token: identifier").
  • Identifiers must start with a letter, underscore (_), or dollar sign ($). They can't start with digits, to prevent confusion between numbers and identifiers:

    let 1a = true;
    
    > Uncaught SyntaxError: Invalid or unexpected token
    

    If JavaScript allowed numbers at the start of an identifier, that would allow identifiers made up of only numbers, causing conflicts between numbers used as numbers and numbers used as identifiers:

    let 10 = 20
    
    10 + 5
    > ?
    
  • "Reserved words" that are already syntactically meaningful can't be used as identifiers.

  • Identifiers can't contain special characters (! . , / \ + - * =).

The following aren't strict rules for creating identifiers, but they are industry best practices that make maintaining your code easier. If your specific project has different standards, follow those instead for consistency.

Following the example set by JavaScript's built-in methods and properties, camel case (also stylized as "camelCase") is a very common convention for identifiers made up of multiple words. Camel case is the practice of capitalizing the first letter of every word except the first for improved readability without spaces.

let camelCasedIdentifier = true;

Some projects use other naming conventions depending on context and the nature of the data. For example, the first letter of a class is typically capitalized, so multi-word class names often use a variant of camel case commonly called "upper camel case" or Pascal case.

class MyClass {

}

Identifiers should concisely describe the nature of the data they contain (for example, currentMonthDays is a better name than theNumberOfDaysInTheCurrentMonth) and read clearly at a glance (originalValue is better than val). The myVariable identifiers used throughout this module work in the context of isolated examples, but would be very unhelpful in production code because they give no information about what data they contain.

Identifiers shouldn't get too specific about the data they contain, because their values can change depending on how scripts act on that data, or on decisions future maintainers make. For example, a variable originally given the identifier miles might need to be changed to a value in kilometers later in the project, requiring maintainers to change any references to that variable to avoid future confusion. To prevent this, use distance as your identifier instead.

JavaScript doesn't give any special privilege or meaning to identifiers that begin with underscore characters (_), but they're typically used to show that a variable, method, or property is "private," meaning that it's only intended for use within the context of the object that contains it, and shouldn't be accessed or modified outside that context. This is a convention carried over from other programming languages, and predates the addition of JavaScript's private properties.

Variable declaration

There are multiple ways to make JavaScript aware of an identifier, a process called "declaring" a variable. A variable is declared using the let, const, or var keywords.

let myVariable;

Use let or var to declare a variable that can be changed at any time. These keywords tell the JavaScript interpreter that a string of characters is an identifier that might contain a value.

When working in a modern codebase, use let instead of var. var still works in modern browsers, but it has some unintuitive behaviors that were defined in the earliest versions of JavaScript, and then couldn't be changed later to preserve backwards compatibility. let was added in ES6 to address some issues with the design of var.

A declared variable is initialized by assigning a value to the variable. Use a single equals sign (=) to assign or reassign a value to a variable. You can do this as part of the same statement that declares it:

let myVariable = 5;

myVariable + myVariable
> 10

You can also declare a variable with let (or var) without initializing it right away. If you do, the variable's initial value is undefined until your code assigns it a value.

let myVariable;

myVariable;
> undefined

myVariable = 5;

myVariable + myVariable
> 10

A variable with an undefined value is different from an undefined variable whose identifier hasn't been declared yet. Referencing a variable you haven't declared causes an error.

myVariable
> Uncaught ReferenceError: myVariable is not defined

let myVariable;

myVariable
> undefined

The association of an identifier with a value is generally called a "binding." The syntax that follows the let, var, or const keywords is called a "binding list," and allows for multiple comma-separated variable declarations (ending with the expected semicolon). This makes the following code snippets functionally identical:

let firstVariable,
     secondVariable,
     thirdVariable;
let firstVariable;
let secondVariable;
let thirdVariable;

Reassigning a variable's value doesn't use let (or var), because JavaScript already knows the variable exists:

let myVariable = true;

myVariable
> true

myVariable = false;

myVariable
> false

You can reassign variables new values based on their existing values:

let myVariable = 10;

myVariable
> 10

myVariable = myVariable * myVariable;

myVariable
> 100

If you try to redeclare a variable using let in a production environment, you'll get a syntax error:

let myVariable = true;
let myVariable = false;
> Uncaught SyntaxError: redeclaration of let myVariable

Browsers' developer tools are more permissive about let (and class) redeclaration, so you might not see the same error in your developer console.

To preserve legacy browser compatibility, var allows unnecessary redeclaration without error in any context:

var myVariable = true;
var myVariable = false;

myVariable\
> false

const

Use the const keyword to declare a constant, a type of variable that must be immediately initialized, and then can't be changed. Identifiers for constants follow all the same rules as variables declared using let (and var):

const myConstant = true;

myConstant
> true

You can't declare a constant without immediately assigning it a value, because constants can't be reassigned after they're created, so any uninitialized constant would stay undefined forever. If you try to declare a constant without initializing it, you get a syntax error:

const myConstant;
Uncaught SyntaxError: missing = in const declaration

Trying to change the value of a variable declared with const the way you might change the value of a variable declared wit with let (or var) causes a type error:

const myConstant = true;

myConstant = false;
> Uncaught TypeError: invalid assignment to const 'myConstant'

However, when a constant is associated with an object, the properties of that object can be altered.

const constantObject = { "firstvalue" : true };

constantObject
> Object { firstvalue: true }

constantObject.secondvalue = false;

constantObject
> Object { firstvalue: true, secondvalue: false }

A constant that contains an object is an immutable reference to a mutable data value. While the constant itself can't be changed, the properties of the referenced object can be altered, added to, or removed:

const constantObject = { "firstvalue" : true };

constantObject = false
> Uncaught TypeError: invalid assignment to const 'constantObject'

When you don't expect a variable to be reassigned, it's best practice to make it a constant. Using const tells your development team or future maintainers of a project not to change that value, to avoid breaking the assumptions your code makes about how it's used—for example, that a variable will eventually be evaluated against an expected data type.

Variable scope

A variable's scope is the part of a script where that variable is available. Outside a variable's scope, it won't be defined—not as an identifier containing an undefined value, but as if it hadn't been declared.

Depending on the keyword you use to declare a variable and the context in which you define it, you can scope variables to block statements (block scope), individual functions (function scope), or the entire JavaScript application (global scope).

Block scope

Any variable you declare using let or const is scoped to its closest containing block statement, meaning that the variable can only be accessed within that block. Trying to access a block scoped variable outside its containing block causes the same error as attempting to access a variable that doesn't exist:

{
    let scopedVariable = true;
    console.log( scopedVariable );
}
> true

scopedVariable
> ReferenceError: scopedVariable is not defined

As far as JavaScript is concerned, a block scoped variable doesn't exist outside the block that contains it. For example, you can declare a constant inside a block, and then declare another constant outside that block that uses the same identifier:

{
  const myConstant = false;
}
const myConstant = true;

scopedConstant;
> true

Although a declared variable can't extend into its parent block, it is available to all descendant blocks:

{
    let scopedVariable = true;
    {
    console.log( scopedVariable );
    }
}
> true

The value of a declared variable can be changed from within a descendant block:

{
    let scopedVariable = false;
    {
    scopedVariable = true;
    }
    console.log( scopedVariable );
}
> true

A new variable can be initialized with let or const inside a descendant block without errors, even if it uses the same identifier as a variable in a parent block:

{
    let scopedVariable = false;
    {
    let scopedVariable = true;
    }
    console.log( scopedVariable );
}
> false

Function Scope

Variables declared using var are scoped to their closest containing function (or static initialization block inside a class).

function myFunction() {
    var scopedVariable = true;

    return scopedVariable;
}

scopedVariable;
> ReferenceError: scopedVariable is not defined

This is still the case after a function has been called. Even though the variable is initialized while the function executes, that variable is still unavailable outside the scope of the function:

function myFunction() {
    var scopedVariable = true;

    return scopedVariable;
}

scopedVariable;
> ReferenceError: scopedVariable is not defined

myFunction();
> true

scopedVariable;
> ReferenceError: scopedVariable is not defined

Global scope

A global variable is available throughout an entire JavaScript application, inside of any and all blocks and functions, to any script on the page.

While this can seem like a desirable default, variables that any part of an application can access and modify can add unnecessary overhead, or even cause collisions with variables elsewhere in an application with the same identifier. This applies to any and all JavaScript involved in the rendering of a page, including things like third-party libraries and user analytics. Therefore, it's best practice to avoid polluting the global scope whenever possible.

Any variable declared using var outside a parent function, or using let or const outside a parent block, is global:

var functionGlobal = true; // Global
let blockGlobal = true; // Global

{
    console.log( blockGlobal );
    console.log( functionGlobal );
}
> true
> true

(function() {
    console.log( blockGlobal );
    console.log( functionGlobal );
}());
> true
> true

Assigning a value to a variable without explicitly declaring it (that is, by never using var, let, or const to create it) elevates a variable to the global scope, even when initialized inside of a function or block. A variable created using this pattern is sometimes called an "implied global."

function myFunction() {
    globalVariable = "global";

    return globalVariable
}

myFunction()\
> "global"

globalVariable\
> "global"

Variable Hoisting

Variables and function declarations are hoisted to the top of their scope, meaning that the JavaScript interpreter processes any variable declared at any point in a script and effectively moves it to the first line of its enclosing scope before executing the script. This means that a variable declared using var can be referenced before the variable is declared without encountering an error:

hoistedVariable
> undefined

var hoistedVariable;

Because only the variable declaration is hosted, not the initialization, variables that haven't been explicitly declared with var, let, or const aren't hoisted:

unhoistedVariable;
> Uncaught ReferenceError: unhoistedVariable is not defined

unhoistedVariable = true;

As mentioned previously, a declared but uninitialized variable is assigned a value of undefined. That behavior applies to hoisted variable declarations as well, but only to those declared using var.

hoistedVariable
> undefined

var hoistedVariable = 2 + 2;

hoistedVariable\
> 4

This unintuitive behavior is largely a holdover from design decisions made in the earliest versions of JavaScript, and can't be changed without the risk of breaking existing sites.

let and const address this behavior by instead throwing an error when a variable is accessed before it's created:

{
    hoistedVariable;

    let hoistedVariable;
}
> Uncaught ReferenceError: can't access lexical declaration 'hoistedVariable' before initialization

This error is different from the "hoistedVariable is not defined" error you might expect when trying to access an undeclared variable. Because JavaScript has hoisted the variable, it's aware that the variable will be created within the given scope. However, instead of making that variable available before its declaration with a value of undefined, the interpreter throws an error. Variables declared with let or const (or class) are said to exist in a "temporal dead zone" ("TDZ") from the start of their enclosing block until the point in the code where the variable is declared.

The temporal dead zone makes the behavior of let more intuitive than var for authors. It's also critical to the design of const. Because constants can't be changed, a constant hoisted to the top of its scope and given an implicit value of undefined couldn't then be initialized with a meaningful value.

Check your understanding

What kinds of characters can you start an identifier with?

A letter
An underscore
A digit

Which is the preferred method of declaring a variable whose value can be changed at any time?

let
const
var