let
, const
Keywords and Block-Level Scope
Problems with Variables Declared Using the var
Keyword
- Until ES5, the only way to declare a variable was by using the
var
keyword. - Variables declared with the
var
keyword have unique characteristics that differentiate them from other languages. If not used carefully, they can cause significant issues.
-
Allowing Duplicate Variable Declarations
var x = 1; var y = 1; // Variables declared with var can be re-declared within the same scope. // If an initializer is present, JavaScript treats it as if the var keyword is omitted. var x = 100; // If no initializer is present, the declaration is ignored. var y; console.log(x); // 100 console.log(y); // 1
-
Function-Level Scope
- Variables declared using var are only scoped to function code blocks.
- → If declared outside a function, they remain global even when declared within a block.
var x = 1; if (true) { // x is a global variable because var does not have block scope. // The existing global x is redeclared, potentially causing unintended value changes. var x = 10; } console.log(x); // 10
var i = 10; // The i declared in the for loop is a global variable, // and the existing i variable is redeclared. for (var i = 0; i < 5; i++) { console.log(i); // 0 1 2 3 4 } // The value of i has changed unintentionally. console.log(i); // 5
- Function-level scope increases the risk of excessive global variables and unintended redeclarations.
-
Variable Hoisting
-
Variables declared with
var
are hoisted, meaning their declarations appear to be moved to the top of the scope.
→ Due to hoisting, variables declared withvar
can be referenced before their declaration, but they will be initialized withundefined
.// At this point, the variable foo has already been declared due to hoisting (1. Declaration phase) // The variable foo is initialized to undefined (2. Initialization phase) console.log(foo); // undefined // Assign a value to the variable (3. Assignment phase) foo = 123; console.log(foo); // 123 // Variable declarations are implicitly executed by the JavaScript engine before runtime. var foo;
-
Although referencing a variable before its declaration does not cause an error, it can lead to readability issues and unexpected behavior.
-
let
Keyword
- To address the shortcomings of
var
, ES6 introducedlet
andconst
. - Let's examine
let
by focusing on its differences fromvar
.
-
Preventing Duplicate Declarations
- Unlike
var
,let
does not allow duplicate declarations within the same scope. → If a variable is redeclared along with an assigned value, it can unintentionally overwrite the previously declared variable's value. - If a variable with the same name is redeclared using the
let
keyword, a syntax error occurs.
var foo = 123; // Variables declared with the `var` keyword can be redeclared within the same scope. // The following variable declaration behaves as if the `var` keyword is omitted by the JavaScript engine. var foo = 456; let bar = 123; // Variables declared with the `let` or `const` keywords do not allow redeclaration within the same scope. let bar = 456; // SyntaxError: Identifier 'bar' has already been declared
- Unlike
-
Block-Level Scope
- Variables declared with
let
are limited to the block where they are declared. - However, variables declared with the
let
keyword follow block-level scope, which means they are recognized as local scope in all code blocks (such as functions, if statements, for loops, while loops, try/catch blocks, etc.).
let foo = 1; // Global variable { let foo = 2; // Local variable let bar = 3; // Local variable } console.log(foo); // 1 console.log(bar); // ReferenceError: bar is not defined
- Variables declared with
-
Variable Hoisting
- Unlike variables declared with the
var
keyword, variables declared with thelet
keyword behave as if variable hoisting does not occur.
console.log(foo); // ReferenceError: foo is not defined let foo;
-
If you reference a variable declared with the
let
keyword before its declaration, a ReferenceError will occur. -
Variables declared with the
var
keyword undergo an implicit "declaration phase" and "initialization phase" by the JavaScript engine before runtime.
→ During the declaration phase, the variable identifier is registered in the scope (lexical environment of the execution context), notifying the JavaScript engine of the variable's existence. Then, in the initialization phase, the variable is initialized withundefined
.
→ Therefore, even if you access the variable before its declaration, no error occurs because the variable exists in the scope. However,undefined
is returned. Once the variable assignment statement is reached, the value is finally assigned.// Variables declared with the `var` keyword undergo the declaration and initialization phases before runtime. // Therefore, they can be referenced before the variable declaration statement. console.log(foo); // undefined var foo; console.log(foo); // undefined foo = 1; // The assignment phase is executed at the assignment statement. console.log(foo); // 1
-
Variables declared with the
let
keyword go through separate declaration and initialization phases.
→ Before runtime, the JavaScript engine implicitly executes the declaration phase first, but the initialization phase is executed only when the variable declaration statement is reached.- f you try to access the variable before the initialization phase is executed, a ReferenceError will occur.
- Variables declared with the
let
keyword cannot be referenced from the beginning of the scope until the initialization phase starts (i.e., the variable declaration statement). - The section from the start of the scope to the start of the initialization phase, where the variable cannot be accessed, is called the Temporal Dead Zone (TDZ).
// Before runtime, the declaration phase is executed, but the variable is not yet initialized. // In the **Temporal Dead Zone (TDZ)** before initialization, the variable cannot be referenced. console.log(foo); // ReferenceError: foo is not defined let foo; // The initialization phase is executed at the variable declaration statement. console.log(foo); // undefined foo = 1; // The assignment phase is executed at the assignment statement. console.log(foo); // 1
-
Variables declared with the
let
keyword appear to not be hoisted. However, this is not actually the case.let foo = 1; // Global variable { console.log(foo); // ReferenceError: Cannot acccess 'foo' before initialization let foo = 2; // Local variable }
- If variables declared with the
let
keyword were not hoisted, the example above should output the value of the global variablefoo
. - However, since variables declared with let are still hoisted, a ReferenceError occurs.
- JavaScript hoists all declarations (
var
,let
,const
,function
,function*
,class
, etc.), including those introduced in ES6. - However, declarations using
let
,const
, andclass
(introduced in ES6) behave as if they are not hoisted.
- If variables declared with the
- Unlike variables declared with the
Global Object and let
-
Global variables declared with the
var
keyword, global functions, and implicitly declared global variables (variables assigned without declaration) become properties of the global objectwindow
. -
When referencing properties of the global object, you can think of
window
as a reference.// This example should be executed in a browser environment. // Global variable var x = 1; // Implicit global y = 2; // Global function function foo() {} // A global variable declared with `var` becomes a property of the global object `window`. console.log(window.x); // 1 // Properties of the global object `window` can be used like global variables. console.log(x); // 1 // An implicit global also becomes a property of the global object `window`. console.log(window.y); // 2 console.log(y); // 2 // A global function declared using a function declaration also becomes a property of the global object `window`. console.log(window.foo); // f foo() {} // Properties of the global object `window` can be used like global variables. console.log(foo); // f foo() {}
-
Global variables declared with the
let
keyword do not become properties of the global object. This means they cannot be accessed usingwindow.foo
, for example. -
Instead,
let
global variables exist within an invisible conceptual block (the declarative environment record of the global lexical environment).// This example should be executed in a browser environment. let x = 1; // Global variables declared with `let` or `const` are not properties of the global object `window`. console.log(window.x); // undefined console.log(x); // 1
const
Keyword
- Used to declare constants, but not limited to constants.
- The
const
keyword is mostly similar to thelet
keyword, so we will focus on the differences.
-
Declaration and Initialization
-
A variable declared with
const
must be initialized at the time of declaration. Otherwise, a syntax error occurs.const foo; // SyntaxError: Missing initializer in const declaration
-
Like variables declared with
let
, variables declared withconst
have block-level scope and behave as if hoisting does not occur.{ // Appears as if hoisting does not occur. console.log(foo); // ReferenceError: Cannot access 'foo' before initialization const foo = 1; console.log(foo); // 1 } // Has block-level scope. console.log(foo); // ReferenceError: foo is not defined
-
-
Reassignment Prohibited
-
Variables declared with
var
orlet
can be freely reassigned, but variables declared withconst
cannot be reassigned.const foo = 1; foo = 2; // TypeError: Assignment to constant variable
-
-
Constants
- When a variable declared with
const
is assigned a primitive value, that value cannot be changed because primitive values are immutable, andconst
prevents reassignment.
→ As a result, there is no way to modify the assigned value.
- When a variable declared with
-
const
Keyword and Objects
-
If a variable declared with
const
is assigned a primitive value, the value cannot be changed. However, if it is assigned an object, the object's properties can be modified.const person = { name: "Lee", }; // Objects are mutable, so modifications are possible without reassignment. person.name = "Kim"; console.log(person); // {name:"Kim"}
-
The
const
keyword only prevents reassignment, but it does not mean immutability.
→ While reassigning a new value is not allowed, modifying an object’s properties—such as adding, deleting, or changing property values—is still possible.
→ Even if the object is modified, the reference value stored in the variable remains unchanged.
var
vs let
vs const
- By default, use
const
for variable declarations. Uselet
only when reassignment is necessary. - Using
const
prevents unintended reassignment, making the code safer. - The recommended usage of
var
,let
, andconst
is as follows:- If using ES6, avoid the
var
keyword. - Use
let
only when reassignment is necessary, and keep the variable’s scope as narrow as possible. - Use
const
for primitive values and objects that are read-only (i.e., values that do not require reassignment). Sinceconst
prevents reassignment, it is safer thanvar
orlet
.
- If using ES6, avoid the
- At the time of declaration, it may not always be clear whether a variable needs reassignment.
→ Therefore, always declare variables with
const
first. If reassignment is later found to be necessary, switching tolet
is not too late.