Variables

One of the most basic concepts of programming is that programs need to store information in memory so that it can use and manipulate that information. Variables are the means for doing that in almost all computer languages. They provide a way to label data with a descriptive name that helps us and other readers understand a program. Think of variables as containers that hold information: their purpose is to label and store data in memory so that your program can use it.

Variables and Variable Names

A variable is simply a named area of a program's memory space where the program can store data. Typically, variables can be changed. That is, we can make a variable point to a different area of memory that has a different value.

Consider this code:

let answer = 41;
answer = 42;
console.log(answer)

When JavaScript sees line 1 of this code, it sets aside a bit of memory and stores the value 41 in that area. It also creates a variable named answer that we can use to access that value.

On line 2, we reassign the value 42 to the variable named answer. That is, JavaScript makes answer refer to a new value. In particular, it's important to realize that we're not changing the value of 41 -- we're putting a completely new value in the answer variable.

Finally, on line 3, we log the value of the answer variable to the JavaScript log. To determine what value it needs to log, JavaScript determines where the value referred to by the answer variable is stored, then retrieves the value stored in that location.

This is a simplified view of how variables work in JavaScript. Later in this book, we'll see some of the complexity involved, and we'll see much more in the Core curriculum.

Variable Naming

Properly naming variables is traditionally viewed as one of the most difficult tasks in computer programming. If you're new to programming, this might seem odd. After all, how hard can it be to choose a name? As it turns out, it can be quite hard. Consider how hard it is for many new parents to choose a name for their baby.

A variable name must accurately and succinctly describe the data that the variable contains. In large programs, it can be extraordinarily difficult to remember what kinds of information each variable contains. If you use non-descriptive names like x, you may forget what data that variable represents. Other readers—programmers—must suss out the meaning for themselves. Therefore, when naming variables, think hard about the names. Try your best to make sure that they are accurate, descriptive, and understandable to other readers. That might be you when you revisit the program a few months or years later.

Variable names are often referred to by the broader term, identifiers. In JavaScript, identifiers refer to several things:

  • Variable names declared by let and var
  • Constant names declared by const
  • Property names of objects
  • Function names
  • Function parameters
  • Class names

You'll meet all of these things as you move through the Launch School Core curriculum.

The term variable name includes all of these identifiers except property names of objects. However, property names of the global object are usually included when discussing variable names. We'll use this inclusive form of variable name, but will be specific when there are important differences.

What Else is a Variable?

JavaScript has a bunch of other things that involve storing data in a named area of memory. The list looks a lot like the list of identifiers:

  • Variables declared with let and var
  • Constants declared with const
  • Properties of the global object
  • Function names
  • Function parameters
  • Class names

Again, you'll meet all of these concepts in this book or later in the curriculum.

The most significant difference in this list compared to the list of identifiers is that not all object properties are variables; only those on the global object.

When JavaScript developers talk about variables or variable names, they often use these more inclusive meanings -- for most purposes, all of these concepts act like variables, and they all have names that can be used in situations that call for variable names. There are differences, though. We'll use the term variables in this inclusive manner, but will be specific when there are important differences.

It may seem a little peculiar talking about constants as variables since constants don't vary. However, in JavaScript, the invariability of constants is usually not important when discussing most characteristics of variables.

JavaScript is also unusual in that we can think of function and class names as being variable names: in fact, they are. Functions and classes are actually values in JavaScript, and their names are used in the same way as more traditional variables.

Declaring and Assigning Variables

A variable declaration is a statement that asks the JavaScript engine to reserve space for a variable with a particular name. Optionally, it also specifies an initial value for the variable. JavaScript supplies several ways to declare variables, but the preferred way in modern JavaScript uses the let keyword:

> let firstName
= undefined

Here, we're telling the interpreter that we want to use a variable named firstName to store some information. If you reference this variable in node, it evaluates as undefined:

> firstName
= undefined

From this, we can see that firstName was initialized with a value of undefined.

To assign a more useful value, we can use an initializer in the declaration:

> firstName = 'Mitchell'
= undefined

> firstName
'Mitchell'

As you can see, we've now stored the string 'Mitchell' in memory. We can use firstName as a label for that string elsewhere in the program:

> console.log(`Your first name is ${firstName}`)
'Your first name is Mitchell'
= undefined

This code uses JavaScript template literals to interpolate the value of firstName into the string before logging it to the console.

Keep in mind that firstName isn't permanently tied to the same string, 'Mitchell'. In fact, we can reassign it to any other value, not just strings:

> firstName = 'Joe'
= 'Joe'

> firstName = 42
= 42

Thus far, we've declared variables and given them an explicit value by entering two lines of code. You don't have to separate them. In fact, in most cases, you'll write your variable declaration and initialize it with an explicit value on the same line:

> let firstName = 'Mitchell'
= undefined

It's interesting to note that the return value of an assignment is the value on the right-hand side of the = operator, but declarations, with or without an initial value, always return undefined.

> firstName = 'Martha'
= 'Martha'

Note that regardless of whether we provide a value in a declaration, the variable is initialized. If we don't provide an explicit value, that initial value is undefined.

There is a subtle difference in terminology surrounding the = token. When used in a declaration, the = is just a syntactic token that tells JavaScript that you're going to supply an initial value for the variable. However, in an assignment, the = is called the assignment operator.

Let's try something else. Look at the following Node session:

> let a = 4
= undefined

> let b = a
= undefined

> a = 7
= 7

> b

What is the value of b at this point? Take your best guess and then type this session into the console to find out.

You'll notice that b retains the value 4, even though a is now 7. This example suggests that variables have values that aren't deeply-linked to each other. If you change one variable, it doesn't change other variables with the same value.

If this is confusing, don't worry: we have a bunch of exercises to help clarify this concept. When in doubt, you can always try running some code in Node.

Declaring Constants

The const keyword is similar to let, but it lets you declare and initialize constant identifiers:

> const firstName = 'Mitchell'
= undefined

> firstName
= Mitchell

Constants have an immutable binding to their values. Unlike an ordinary variable, once you declare a constant, you cannot assign it a new value. The constant will continue to point to that value until the constant is no longer needed.

const INTEREST_RATE = 0.0783;
INTEREST_RATE = 0.0788; // Uncaught TypeError: Assignment to constant variable.

Using constants is a great way to label a value with a name that makes your code more descriptive and easier to understand. For instance, which of the following code snippets is easier to understand?

let interest = amount * 0.0783;
const INTEREST_RATE = 0.0783;
let interest = amount * INTEREST_RATE;

Most developers will say that the second snippet is easier to understand -- 0.0783 is clearly identified as an interest rate. In the first snippet, it may not be entirely clear what 0.0783 means.

A standard convention when naming constants is to use all uppercase letters and digits in the name; if the name contains multiple words, use underscores to separate the words.

Note that const declarations require a value:

const foo; // SyntaxError: Missing initializer in const declaration

When we use the terms variable or variable name, we are usually including constants declared by const in that same discussion.

Variable Scope

A variable's scope determines where it is available in a program. The location where you declare a variable determines its scope. In JavaScript, variables declared with the let or const keywords have block scope. A block is a related set of JavaScript statements and expressions between a pair of opening and closing curly braces. We'll use an if statement to illustrate since they typically use at least one block:

if (expression) {  // block starts at {
  doSomething();   // block body
}                  // block ends here

This code uses the basic syntax of an if statement: we'll learn more in the "Flow Control" chapter. For now, all you need to know is that JavaScript evaluates the expression between the parentheses (()). If it evaluates as true, JavaScript executes the code inside the block. Otherwise, it skips to the code that follows the block. Here, we run doSomething() when expression evaluates as true.

Not everything between curly braces is technically a block. For instance, the braces that surround an object literal do not define a block. Technically, the braces that surround a function body don't define a block either, though it is convenient to think of function bodies as blocks. While there are similarities between blocks, function bodies, and, to a lesser degree, object literals, the term block usually refers to executable code between braces, including function bodies:

{
  // this is a block
  let foo = 42;
  console.log(foo);
}

if (answer === 'yes') {
  // this is a block
  console.log('yes');
} else {
  // so is this
  console.log('nope');
}

while (answer !== 'no') {
  // this is a block
  doSomething();
}

function foo {
  // not technically a block. However, we can treat is as a block.
  let foo = 42;               // foo has block scope
  console.log(foo);
}

let foo = {
  // this is not a block
  bar: 42,
};

In general, blocks appear in if...else if...else, while, do...while, for, switch, and try...catch statements, or by themselves (as in the first example above).

As mentioned above, function bodies are not technically blocks. However, they look and behave so much like blocks that many developers do not distinguish between the two. In this book and the curriculum, we'll usually treat function bodies as blocks. When we want to differentiate them or exclude function bodies from the discussion, we'll refer to non-function blocks.

In the next example, the condition always evaluates as true, so JavaScript runs the let statement on line 2. That code declares a variable a and assigns it to the string 'foo'. However, we get an error on line 5 since let creates a block-scoped variable; a isn't accessible outside the block.

if (1 === 1) {
  let a = 'foo';
}

console.log(a); // ReferenceError: a is not defined

The error tells you that a isn't available on line 5. In other words, it isn't in scope outside of the if block.

If, on the other hand, you declare the variable outside the if block, the variable is available within the block as well as after the block ends.

let a = 'foo';
if (1 === 1) {
  a = 'bar';
}

console.log(a);    // => 'bar'

As we can see, this code prints the string "bar" since a is accessible inside the block. Thus, we can reassign it to a different value inside the block. In other words, this a has a broader scope than the a variable in the previous example.

Constants declared with const have the same scope as variables declared with let.

There's a third type of variable declaration that uses the var keyword and doesn't use block-scoping. We discuss var and variable scoping in more detail in the Launch School courses. For now, let, const, and block scope are the important takeaways of this section.

A Common Gotcha

Be sure to always declare your variables and constants with let and const. JavaScript is a forgiving language, and one of the ways it demonstrates that occurs when you fail to declare a variable or constant. You can create them willy-nilly merely by assigning a variable to a value:

p = 'foo';

That looks harmless, but JavaScript has some bizarre rules when working with undeclared variables. The most notable rule is that all undeclared variables have global scope: they ignore block and function scope entirely. If your program uses that same variable name in a different scope without declaring it, there's a good chance that it will step on the original variable by changing its content. You don't want that to happen: it's typically difficult to debug, and sometimes fixing it breaks other code.

We discuss this gotcha in more detail in the Core Curriculum.

Summary

In this chapter, we've learned how to use variables to store information for later use. We also learned that not all variables have the same scope: where you define the variable determines the scope in which you can use and modify it.

Since you now know how to use variables and constants, let's put that knowledge to work with some exercises.

Exercises

  1. Write a program named greeter.js that greets 'Victor' three times. Your program should use a variable and not hard code the string value 'Victor' in each greeting. Here's an example run of the program:

    $ node greeter.js
    Good Morning, Victor.
    Good Afternoon, Victor.
    Good Evening, Victor.
    

    Solution

    let name = 'Victor';
    console.log('Good Morning, ' + name + '.');
    console.log('Good Afternoon, ' + name + '.');
    console.log('Good Evening, ' + name + '.');
    
    let name = 'Victor';
    console.log(`Good Morning, ${name}.`);
    console.log(`Good Afternoon, ${name}.`);
    console.log(`Good Evening, ${name}.`);
    

    First, we create a variable that holds the value 'Victor'. With a variable, we don't need to hard code 'Victor' each time we greet him. Instead, we can use the variable as part of an expression. In our first solution, we use string concatenation; in the second, we use template literals.

  2. Write a program named age.js that includes someone's age and then calculates and reports the future age in 10, 20, 30 and 40 years. Below is the output for someone 20 years old.

    You are 20 years old.
    In 10 years, you will be 30 years old.
    In 20 years, you will be 40 years old.
    In 30 years, you will be 50 years old.
    In 40 years, you will be 60 years old.
    

    Solution

    let age = 20;
    console.log(`You are ${age} years old.`);
    console.log(`In 10 years, you will be ${age + 10} years old.`);
    console.log(`In 20 years, you will be ${age + 20} years old.`);
    console.log(`In 30 years, you will be ${age + 30} years old.`);
    console.log(`In 40 years, you will be ${age + 40} years old.`);
    
  3. What happens when you run the following program? Why do we get that result?

    {
      let foo = 'bar';
    }
    
    console.log(foo);
    

    Solution

    The program outputs an error since foo is out of scope: the let statement creates variables with block scope, which limits the visibility of the variable to the block. Even though console.log(foo) comes after the declaration and initialization of foo, we still get an error since we declared foo inside the block. Outside the block, foo doesn't exist.

  4. What happens when you run the following code? Why?

    const NAME = 'Victor';
    console.log('Good Morning, ' + NAME);
    console.log('Good Afternoon, ' + NAME);
    console.log('Good Evening, ' + NAME);
    
    NAME = 'Joe';
    console.log('Good Morning, ' + NAME);
    console.log('Good Afternoon, ' + NAME);
    console.log('Good Evening, ' + NAME);
    

    Solution

    The program first greets Victor 3 times. It then encounters an error on line 6, which prevents it from greeting Joe. The problem is that constants are, well, constant. You can't reassign a constant after defining it. You must use regular variables instead:

    let name = 'Victor';
    console.log('Good Morning, ' + name);   // => Good Morning, Victor
    console.log('Good Afternoon, ' + name); // => Good Afternoon, Victor
    console.log('Good Evening, ' + name);   // => Good Evening, Victor
    
    name = 'Joe';                           // no error!
    console.log('Good Morning, ' + name);   // => Good Morning, Joe
    console.log('Good Afternoon, ' + name); // => Good Afternoon, Joe
    console.log('Good Evening, ' + name);   // => Good Evening, Joe
    
  5. Take a look at this code snippet:

    let foo = 'bar';
    {
      let foo = 'qux';
    }
    
    console.log(foo);
    

    What does this program log to the console? Why?

    Solution

    The program logs bar.

    Line 1 initializes a variable named foo with the value'bar'. Line 2 starts a block, which creates a new scope for let variables. The variable on line 1 is still visible at this point, but line 3 declares a new variable named foo that shadows (hides) the variable from line 1. This second variable gets initialized to 'qux', but it goes out of scope on line 4 when the block ends. That brings foo from line 1 back into scope, so line 6 logs its value: bar.

  6. Will this program produce an error when run? Why or why not?

    const FOO = 'bar';
    {
      const FOO = 'qux';
    }
    
    console.log(FOO);
    

    Solution

    For much the same reason as the previous exercise, this program doesn't raise an error, and it logs bar on line 6. One key difference, though, is that we are using const instead of let, which may have led you to believe an error would occur on line 3. However, since the two const variables are separate entities, no error occurs.