One of the most basic concepts of programming is that a program needs to store information in memory so that it can use and manipulate that information across the program. Variables are the means for doing that in almost all computer languages. Variables provide a way to label your data with a descriptive name. This helps us and other readers understand a program. You can 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.
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 give the variable a new 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 assigning a completely new value to 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 retrieves the value stored in the location used by the variable.
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.
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:
let
and var
const
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.
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:
let
and var
const
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.
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:
> let 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. On the other hand, while the declaration looks like it's returning undefined
in this example, declarations don't actually return a value at all. This is because assignments and reassignments are expressions, and declarations are statements. Statements don't return values. This example only looks like it's returning undefined
because that's what Node's REPL is programmed to do.
> 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. For example, in let firstName = 'Mitchell'
, the =
is a syntactic token, but in firstName = 'Martha'
, it is 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.
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 have 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.
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 it 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
, 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 them. In this book and the curriculum, we'll usually treat function bodies as blocks when we're discussing block-scoped variables. However, in most other cases, function and block scope should be thought of as different kinds of scope.
We'll discuss scope in much more detail in a later course. In particular, we will clarify the various different kinds of scope and see how they relate to each other.
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.
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.
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.
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.
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.
Video Walkthrough
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.
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.`);
Video Walkthrough
What happens when you run the following program? Why do we get that result?
{
let foo = 'bar';
}
console.log(foo);
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.
Video Walkthrough
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);
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
Video Walkthrough
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?
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
.
Video Walkthrough
Will this program produce an error when run? Why or why not?
const FOO = 'bar';
{
const FOO = 'qux';
}
console.log(FOO);
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 -- that is, the declaration on line 3 declares a completely new constant -- no error occurs.
Video Walkthrough