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 ofmyVariable
, the parser sees two identifiers,my
andVariable
, 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?
Which is the preferred method of declaring a variable whose value can be changed at any time?