Type |
Hoisting Behavior |
var |
Hoisted to the top of the scope but initialized as undefined. |
let |
Hoisted but not initialized. Accessing it before declaration causes a ReferenceError. |
const |
Hoisted but not initialized. Must be initialized at the time of declaration. |
Type |
Hoisting Behavior |
var
|
Hoisted to the top of the scope but initialized as undefined. |
let// Re-declaration
var a = 10;
var a = 20; // Allowed
let b = 30;
// let b = 40; // SyntaxError: Identifier 'b' has already been declared
const c = 50;
// const c = 60; // SyntaxError: Identifier 'c' has already been declared
// Re-assignment
a = 15; // Allowed
b = 35; // Allowed
// c = 55; // TypeError: Assignment to constant variable
|
Hoisted but not initialized. Accessing it before declaration causes a ReferenceError. |
const
|
Hoisted but not initialized. Must be initialized at the time of declaration. |
Example:
Feature |
let and const
|
Block Scope |
Both are confined to the block in which they are declared. |
No Hoisting Initialization |
Both are hoisted but cannot be accessed before initialization. |
Better Practice |
Preferred over var for predictable scoping. |
5. Similarities Between let and const
Scenario |
Recommended Keyword |
Re-declare variables or use function scope |
var (generally avoid unless necessary for legacy code). |
Variables that may change |
let (e.g., counters, flags, intermediate calculations). |
Variables that should not change |
const (e.g., configuration settings, fixed values). |
Feature |
let and const
|
Block Scope |
Both are confined to the block in which they are declared. |
No Hoisting Initialization |
Both are hoisted but cannot be accessed before initialization. |
Better Practice |
Preferred over var for predictable scoping. |
6. When to Use Which?
Scenario |
Recommended Keyword |
Re-declare variables or use function scope |
var (generally avoid unless necessary for legacy code). |
Variables that may change |
let (e.g., counters, flags, intermediate calculations). |
Variables that should not change |
const (e.g., configuration settings, fixed values). |
7. Explanation of Hoisting
What is Hoisting?
Hoisting is JavaScript's default behavior of moving declarations to the top of their scope during the compile phase.
-
var: Hoisted and initialized to undefined.
-
let / const: Hoisted but not initialized. This creates a temporal dead zone (TDZ) from the start of the block until the declaration is encountered.
Why Hoisting Works This Way?
-
Compilation Phase:
JavaScript first scans the code to create a memory space for variable and function declarations. At this stage:
-
var variables are initialized to undefined.
-
let and const variables are "hoisted" but left uninitialized, hence the TDZ.
- Function declarations are fully hoisted.
-
Execution Phase:
JavaScript starts executing code line by line. Variables and functions are assigned values during this phase.
8. Summary of Hoisting
Type |
Hoisting |
Access Before Declaration |
var |
Hoisted and initialized to undefined. |
Allowed but value is undefined. |
let |
Hoisted but not initialized. |
Causes a ReferenceError. |
const |
Hoisted but not initialized. |
Causes a ReferenceError. |
Type |
Hoisting |
Access Before Declaration |
var |
Hoisted and initialized to undefined. |
Allowed but value is undefined. |
let |
Hoisted but not initialized. |
Causes a ReferenceError. |
const |
Hoisted but not initialized. |
Causes a ReferenceError. |
Example:
if (true) {
var x = 10;
let y = 20;
const z = 30;
}
console.log(x); // 10 (accessible because of function scope)
console.log(y); // ReferenceError (block-scoped)
console.log(z); // ReferenceError (block-scoped)
Conclusion
- Use const whenever possible for variables that do not need reassignment.
- Use let for variables that need to be reassigned within the same scope.
- Avoid var unless working with legacy code or requiring function-scoped behavior.
JavaScript Data Types
JavaScript has various data types classified into Primitive and Non-Primitive (Reference) types. Here's an explanation of each with examples and differences:
1. Primitive Data Types
Primitive types are immutable, meaning their values cannot be changed after they are created. They are stored directly in memory.
Data Type |
Example |
Description |
String |
"hello", 'world'
|
Represents a sequence of characters (text). Enclosed in single (''), double (""), or backticks (). |
Number |
42, 3.14, NaN
|
Represents both integers and floating-point numbers. Includes NaN (Not-a-Number) and Infinity. |
BigInt |
123n, 9007199254740991n
|
Used for numbers larger than Number.MAX_SAFE_INTEGER (2^53 - 1). Add n to create a BigInt. |
Boolean |
true, false
|
Represents logical values, used in conditions to represent "yes/no" or "on/off". |
Undefined |
undefined |
Indicates a variable has been declared but not assigned a value. |
Null |
null |
Represents an intentional absence of value. Often used to reset or clear a variable. |
Symbol |
Symbol('id') |
Represents a unique identifier, mainly used as property keys for objects to avoid collisions. |
Data Type |
Example |
Description |
String |
"hello", 'world'
|
Represents a sequence of characters (text). Enclosed in single (''), double (""), or backticks (). |
Number |
42, 3.14, NaN
|
Represents both integers and floating-point numbers. Includes NaN (Not-a-Number) and Infinity. |
BigInt |
123n, 9007199254740991n
|
Used for numbers larger than Number.MAX_SAFE_INTEGER (2^53 - 1). Add n to create a BigInt. |
Boolean |
true, false
|
Represents logical values, used in conditions to represent "yes/no" or "on/off". |
Undefined |
undefined |
Indicates a variable has been declared but not assigned a value. |
Null |
null |
Represents an intentional absence of value. Often used to reset or clear a variable. |
Symbol |
Symbol('id') |
Represents a unique identifier, mainly used as property keys for objects to avoid collisions. |
2. Non-Primitive (Reference) Data Types
Non-primitive types are mutable and stored by reference. They are used to store collections of data or more complex entities.
Data Type |
Example |
Description |
Object |
{name: 'John', age: 30} |
A collection of key-value pairs. Keys are strings (or Symbols), and values can be any type. |
Array |
[1, 2, 3, "apple"] |
A list-like ordered collection of values. Access elements via index (e.g., array[0]). |
Function |
function greet() {} |
A reusable block of code that can be executed. Functions are first-class citizens in JavaScript. |
Date |
new Date() |
Represents date and time. Provides methods for manipulating dates and times. |
RegExp |
/pattern/ |
Represents regular expressions used for pattern matching and string searching. |
Map |
new Map() |
A collection of key-value pairs where keys can be of any type, unlike plain objects. |
Set |
new Set([1, 2, 3]) |
A collection of unique values, preventing duplicates. |
WeakMap |
new WeakMap() |
Similar to Map, but keys are weakly held, meaning they can be garbage-collected. |
WeakSet |
new WeakSet() |
Similar to Set, but holds objects weakly to prevent memory leaks. |
3. Key Differences Between Primitive and Non-Primitive Types
Aspect |
Primitive Types |
Non-Primitive Types |
Mutability |
Immutable: Values cannot be changed. |
Mutable: Values can be modified. |
Storage |
Stored directly in memory. |
Stored as a reference to a memory location. |
Copy Behavior |
Copied by value (creates a new value). |
Copied by reference (points to the same object). |
Examples |
string, number, boolean, etc. |
object, array, function, etc. |
4. Special Cases
typeof Operator
-
typeof null: Returns "object" due to a historical bug in JavaScript, but null is not an object.
-
typeof NaN: Returns "number", even though it means "Not-a-Number."
-
typeof function: Returns "function", which is a subtype of object.
Dynamic Typing
JavaScript allows variables to hold values of different types at runtime:
if (true) {
var x = 10;
let y = 20;
const z = 30;
}
console.log(x); // 10 (accessible because of function scope)
console.log(y); // ReferenceError (block-scoped)
console.log(z); // ReferenceError (block-scoped)
5. Examples for Each Data Type
Primitive Types
// Re-declaration
var a = 10;
var a = 20; // Allowed
let b = 30;
// let b = 40; // SyntaxError: Identifier 'b' has already been declared
const c = 50;
// const c = 60; // SyntaxError: Identifier 'c' has already been declared
// Re-assignment
a = 15; // Allowed
b = 35; // Allowed
// c = 55; // TypeError: Assignment to constant variable
Non-Primitive Types
console.log(a); // undefined (hoisted)
var a = 10;
console.log(b); // ReferenceError (temporal dead zone)
let b = 20;
console.log(c); // ReferenceError (temporal dead zone)
const c = 30;
6. Summary of typeof Results
Expression |
Result |
typeof "hello" |
"string" |
typeof 42 |
"number" |
typeof 123n |
"bigint" |
typeof true |
"boolean" |
typeof undefined |
"undefined" |
typeof null |
"object" |
typeof Symbol() |
"symbol" |
typeof {} |
"object" |
typeof [] |
"object" |
typeof function(){} |
"function" |
Expression |
Result |
typeof "hello" |
"string" |
typeof 42 |
"number" |
typeof 123n |
"bigint" |
typeof true |
"boolean" |
typeof undefined |
"undefined" |
typeof null |
"object" |
typeof Symbol() |
"symbol" |
typeof {} |
"object" |
typeof [] |
"object" |
typeof function(){} |
"function" |