Building Blocks of JavaScript: Mastering Variables, Data Types, and Control Flow
A Beginner's Guide to JavaScript Foundations
Variables & Scopes
In JavaScript, variables are used to store and manipulate data. They act as named storage for values that can be reused or modified throughout a program. Understanding variables is crucial since they are the building blocks for any JavaScript application.
1. Declaring Variables
JavaScript provides three ways to declare variables: var
, let
, and const
. Each has distinct characteristics regarding scope, reassignability, and hoisting.
a. var
Declaration
Introduced in early versions of JavaScript (ES1).
Function-scoped: If a variable is declared inside a function, it can only be accessed within that function. If declared outside, it’s attached to the global object.
Hoisting:
var
declarations are hoisted to the top of their scope (function or global), but their initialization remains in place.console.log(x); // undefined (hoisted, but not initialized) var x = 10; console.log(x); // 10
Re-declaration and reassignment:
var
allows re-declaration of the same variable and reassignment of values.var a = 5; var a = 10; // No error, allowed in non-strict mode a = 20; // Reassignment is also allowe
b. let
Declaration
Introduced in ES6 (2015).
Block-scoped: Variables declared with
let
are limited to the block (a pair of curly braces{}
) in which they are defined.Hoisting:
let
is also hoisted, but it’s in a “temporal dead zone” from the top of its scope until the line of declaration. Accessing it before declaration results in aReferenceError
.console.log(y); // ReferenceError: Cannot access 'y' before initialization let y = 10; console.log(y); // 10
Re-declaration:
let
does not allow re-declaring the same variable in the same scope but allows reassignment.let b = 5; // let b = 10; // SyntaxError: Identifier 'b' has already been declared b = 10; // Reassignment is allowed
c. const
Declaration
Introduced in ES6 (2015).
Block-scoped like
let
.Constant: Variables declared with
const
must be initialized during declaration and cannot be reassigned.Hoisting: Like
let
,const
is hoisted but exists in the temporal dead zone until initialized.const z = 20; // z = 25; // TypeError: Assignment to constant variable.
Immutability: While the binding is constant, the data stored in objects or arrays declared with
const
can still be modified (mutability within the object or array).const arr = [1, 2, 3]; arr.push(4); // Allowed, because the array's content can change console.log(arr); // [1, 2, 3, 4] const obj = {name: "Alice"}; obj.name = "Bob"; // Allowed, because object properties can change console.log(obj); // {name: "Bob"}
2. Variable Scope
Scope determines the accessibility of variables in different parts of the code.
a. Global Scope
Variables declared outside any function or block are in the global scope.
In browsers, global variables are added as properties of the
window
object.var globalVar = 100; console.log(window.globalVar); // 100
Using
let
andconst
to declare global variables doesn’t attach them to thewindow
object.let globalLet = 50; console.log(window.globalLet); // undefined
b. Function Scope (for var
)
Variables declared with
var
inside a function are limited to that function.function test() { var localVar = 10; console.log(localVar); // 10 } console.log(localVar); // ReferenceError: localVar is not defined
c. Block Scope (for let
and const
)
Variables declared with
let
orconst
inside blocks ({}
) are limited to that block.This makes
let
andconst
better suited for block-scoped operations likefor
loops and conditional statements.if (true) { let blockScoped = "I'm block scoped!"; const alsoBlockScoped = "Me too!"; } console.log(blockScoped); // ReferenceError
3. Hoisting
Hoisting is JavaScript’s behavior of moving declarations to the top of their scope before code execution.
var
: Hoisted with an undefined value until the point of assignment.let
andconst
: Hoisted but not initialized, leading to the temporal dead zone (where accessing them before declaration throws an error).console.log(a); // undefined (due to hoisting) var a = 10; console.log(b); // ReferenceError: Cannot access 'b' before initialization let b = 10;
4. Reassigning and Re-declaring Variables
Reassignment is changing the value of a variable after it has been declared.
Re-declaration is declaring a variable again within the same scope.
var
: Allows both reassignment and re-declaration.let
: Allows reassignment but not re-declaration in the same scope.const
: Allows neither reassignment nor re-declaration.
Conclusion
Understanding how to declare, scope, and use variables in JavaScript is crucial for writing clean, maintainable, and bug-free code. The introduction of let
and const
in ES6 has provided safer and more predictable variable behavior compared to the older var
.
Data Types
A data type is an abstract idea that helps us group data according to its value and the whole set of operations possible on that data.
JavaScript comes equipped with 7 primitive data types: undefined, null, numbers, strings, Booleans, Symbols, and BigInt.
JavaScript divides data essentially into two main categories: primitives and objects (a composite type).
A primitive is one of the simplest forms of data. The key characteristic of primitives is that they don't have any properties/methods attached to them.
An object is a value composed of primitives. An object, unlike a primitive, can have properties/methods attached to it.
An object is sometimes also referred to as a reference. And by that means, the object type is also known as the reference type.
Simply put, if a value isn't a primitive in JavaScript, then it is an object. Apart form the seven primitive data types, everything else is an object in the language.
Primitive Data Types
Primitive data types are immutable and hold a single value. They are:
Number
String
Boolean
Undefined
Null
Symbol (introduced in ES6)
BigInt (introduced in ES2020)
1. Number
The Number
type is used for both integers and floating-point numbers (decimals).
- JavaScript only has one number type, represented as a 64-bit floating-point number (IEEE 754 standard).
Examples:
const intNum = 42; // Integer
const floatNum = 3.14; // Floating-point
const negNum = -100; // Negative number
const exponent = 2e5; // 2 * 10^5 = 200000
Special Numbers:
Infinity
: Positive and negative infinity.console.log(1 / 0); // Infinity console.log(-1 / 0); // -Infinity
NaN
: Stands for "Not-a-Number" and occurs when an invalid mathematical operation is performed.console.log(0 / 0); // NaN
2. String
The String
type represents a sequence of characters (text). Strings are enclosed in single quotes ('...'
), double quotes ("..."
), or template literals (...
).
Examples:
const str1 = 'Hello';
const str2 = "World";
const str3 = `Hello, ${str2}!`; // Template literals with interpolation
console.log(str3); // Output: Hello, World!
3. Boolean
The Boolean
type has two possible values: true
or false
. Booleans are commonly used in logical operations and conditional statements.
Examples:
const isTrue = true;
const isFalse = false;
console.log(5 > 3); // true
console.log(5 < 3); // false
4. Undefined
The undefined
type represents a variable that has been declared but not assigned a value yet. It’s automatically assigned by JavaScript to any variable without a defined value.
Example:
let notAssigned;
console.log(notAssigned); // undefine
5. Null
null
represents the intentional absence of any object value. It is different from undefined
because undefined
indicates the lack of assignment, while null
indicates an intentional "empty" value.
Example:
let emptyValue = null;
console.log(emptyValue); // null
6. Symbol (ES6)
Symbol
is a unique and immutable data type used to create unique identifiers for object properties. Even if two symbols have the same description, they are guaranteed to be unique.
Example:
const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // false, they are unique
7. BigInt (ES2020)
BigInt
is a numeric type that can represent integers with arbitrary precision. It is used when dealing with very large integers that exceed the safe integer limit for the Number
type (2^53 - 1
).
Example:
const bigInt1 = 1234567890123456789012345678901234567890n; // BigInt literal with 'n'
const bigInt2 = BigInt(123456789012345678901234567890); // Using BigInt constructor
console.log(bigInt1 + bigInt2); // BigInt arithmetic
Non-Primitive Data Type
Objects
Objects are the most complex and versatile data type in JavaScript. They are a collection of properties and methods, where each property is a key-value pair. Objects can hold any type of value, including other objects, arrays, functions, etc.
Examples of Objects:
Plain Object
const person = {
name: "Alice",
age: 25,
greet: function() {
console.log("Hello, " + this.name);
}
};
person.greet(); // Output: Hello, Alice
Array (a type of Object)
Arrays are objects that store ordered collections of values (of any type).
const arr = [1, 2, 3, "four", { five: 5 }];
console.log(arr[3]); // Output: "four"
Function (a type of Object)
Functions in JavaScript are also objects and can have properties. However, their primary purpose is to define reusable blocks of code.
function add(a, b) {
return a + b;
}
console.log(add(5, 3)); // Output:
Dynamic Typing
JavaScript is a dynamically typed language, which means that variables do not have types. Instead, the values they hold have types, and you can reassign variables to hold values of different types.
Example:
let dynamicVar = 42; // Number type
dynamicVar = "Hello"; // Now it's a String
console.log(typeof dynamicVar); // Output: "string"
Type Conversion
1. Implicit Type Coercion
JavaScript automatically converts values to the expected type in certain operations. This is called implicit type coercion.
Example:
console.log(5 + "5"); // Output: "55" (Number is coerced to String)
console.log("5" * 2); // Output: 10 (String is coerced to Number)
2. Explicit Type Conversion
You can explicitly convert values between types using methods like String()
, Number()
, and Boolean()
.
Example:
const num = 123;
const str = String(num); // Converts Number to String
console.log(typeof str); // Output: "string"
const strToNum = Number("123"); // Converts String to Number
console.log(typeof strToNum); // Output: "number"
Type Checking
You can check the type of a value using the typeof
operator.
Examples:
console.log(typeof 42); // "number"
console.log(typeof "Hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (this is a known quirk in JavaScript)
console.log(typeof Symbol()); // "symbol"
console.log(typeof 123n); // "bigint"
console.log(typeof {}); // "object"
console.log(typeof function(){}); // "function"
Note that null
is considered an object due to a historical bug in JavaScript. However, you can distinguish null
from objects using a more specific check:
console.log(null === null); // true
console.log(null === {}); // false
<aside> ℹ️
How can a primitive in JavaScript have a property on it? Via autoboxing.
Autoboxing is simply when JavaScript automatically boxes, i.e. wraps, a primitive value into an object based on its corresponding wrapper class.
Essentially, excluding undefined and null, JavaScript defines wrapper classes (sometimes also referred to as wrapper types) for all primitive types, used to denote corresponding objects of those types. The nomenclature of these objects is also pretty intuitive.
So, for example, suppose that str
is a variable holding a string. Accessing a property on str
, like str.length
would autobox str
into a String
object and then access the property on the object before deleting the object.
How exactly autoboxing is performed is completely implementation-dependent. But it's definitely a thing in JavaScript, and likewise, worth knowing.
</aside>
Conclusion
Primitive Types: Number, String, Boolean, Undefined, Null, Symbol, and BigInt.
Non-Primitive Types: Objects (including Arrays, Functions).
JavaScript is dynamically typed, and values can be implicitly or explicitly converted between types.
Type checking is done using
typeof
, though some quirks exist (likenull
being reported as an object).
Understanding these types is crucial for mastering JavaScript's behavior and interactions, especially in terms of memory, performance, and type coercion.
Operators
In JavaScript, operators are used to perform operations on values (variables, constants, literals, or expressions). They can be classified into different categories based on their behavior.
1. Arithmetic Operators
Arithmetic operators are used to perform mathematical operations.
Operator | Description | Example |
+ | Addition | 5 + 3 → 8 |
- | Subtraction | 5 - 3 → 2 |
* | Multiplication | 5 * 3 → 15 |
/ | Division | 9 / 3 → 3 |
% | Modulus (Remainder) | 10 % 3 → 1 |
++ | Increment (adds 1) | let a = 5; a++ → 6 |
-- | Decrement (subtracts 1) | let a = 5; a-- → 4 |
Unary Plus and Minus
Unary Plus (
+
): Converts its operand into a number (if it’s not already a number).+"5"; // 5 +"true"; // NaN (Not a Number)
Unary Minus (``): Converts its operand into a number and negates it.
-"5"; // -5
2. Assignment Operators
Assignment operators assign values to variables.
Operator | Description | Example |
= | Assign | x = 5 |
+= | Add and assign | x += 3 (equivalent to x = x + 3 ) |
-= | Subtract and assign | x -= 2 |
*= | Multiply and assign | x *= 4 |
/= | Divide and assign | x /= 2 |
%= | Modulus and assign | x %= 3 |
3. Comparison Operators
Comparison operators compare two values and return a boolean (true
or false
).
Operator | Description | Example |
== | Equal to (loose equality) | 5 == '5' → true |
=== | Strict equal to (checks type and value) | 5 === '5' → false |
!= | Not equal to (loose inequality) | 5 != '6' → true |
!== | Strict not equal to (checks type and value) | 5 !== '5' → true |
> | Greater than | 10 > 5 → true |
< | Less than | 10 < 5 → false |
>= | Greater than or equal to | 5 >= 5 → true |
<= | Less than or equal to | 5 <= 10 → true |
Loose vs. Strict Equality
==
performs type coercion (converts operands to the same type before comparing).'5' == 5; // true true == 1; // true
===
(strict equality) compares both the value and the type.'5' === 5; // false true === 1; // false
4. Logical Operators
Logical operators are used for logical operations, primarily with boolean values.
Operator | Description | Example |
&& | Logical AND (returns true if both are true) | true && false → false |
` | ` | |
! | Logical NOT (inverts the value) | !true → false |
Short-Circuit Evaluation
&&
: Stops evaluation as soon as the first false operand is encountered.false && (expression); // expression is not evaluated
||
: Stops evaluation as soon as the first true operand is encountered.true || (expression); // expression is not evaluated
5. Bitwise Operators
Bitwise operators work on the binary (bit) level of numbers.
Operator | Description | Example |
& | AND | 5 & 1 → 1 |
` | ` | OR |
^ | XOR | 5 ^ 1 → 4 |
~ | NOT | ~5 → -6 |
<< | Left shift | 5 << 1 → 10 |
>> | Right shift | 5 >> 1 → 2 |
>>> | Zero-fill right shift | 5 >>> 1 → 2 |
6. Ternary (Conditional) Operator
The ternary operator is a shorthand for if-else
conditions. It has three operands: a condition followed by a question mark (?
), then an expression for the true
case and one for the false
case.
let age = 18;
let canVote = (age >= 18) ? "Yes" : "No";
console.log(canVote); // "Yes"
Control Flow
In JavaScript, control flow refers to the order in which individual statements, instructions, or function calls are executed in a program. By default, the control flow in JavaScript follows a top-to-bottom, left-to-right approach, meaning that statements are executed one after another in the order they are written. However, control flow can be altered using control flow statements to make decisions, repeat blocks of code, or jump to different parts of the code.
Conditional Statements
Conditional statements allow the code to execute different paths based on certain conditions.
a. if
Statement
The if
statement executes a block of code if a specified condition is true
.
let age = 18;
if (age >= 18) {
console.log("You are eligible to vote.");
}
b. if-else
Statement
The if-else
statement allows you to execute one block of code if the condition is true
, and another block if the condition is false
.
let age = 16;
if (age >= 18) {
console.log("You are eligible to vote.");
} else {
console.log("You are not eligible to vote.");
}
c. else if
Statement
The else if
statement is used when you need to check multiple conditions. The first if
block that evaluates to true
gets executed, and the rest are ignored.
let score = 75;
if (score >= 90) {
console.log("Grade: A");
} else if (score >= 80) {
console.log("Grade: B");
} else if (score >= 70) {
console.log("Grade: C");
} else {
console.log("Grade: F");
}
d. Ternary (Conditional) Operator
The ternary operator is a shorthand way to write simple if-else
statements. It uses the syntax condition ? expressionIfTrue : expressionIfFalse
.
let age = 18;
let canVote = (age >= 18) ? "Yes" : "No";
console.log(canVote); // "Yes"
e. switch
Statement
The switch
statement is used to perform different actions based on different values of a single expression. It's often an alternative to else if
chains when dealing with multiple possible values for a variable.
let day = 3;
switch (day) {
case 1:
console.log("Monday");
break;
case 2:
console.log("Tuesday");
break;
case 3:
console.log("Wednesday");
break;
default:
console.log("Invalid day");
}
break
: Thebreak
statement prevents the code from falling through to the next case.default
: Thedefault
block is executed if no cases match the expression.
Conclusion
Control flow in JavaScript determines how the program moves from one statement to another. Using conditional statements, loops, error handling, and asynchronous constructs allows you to manage the order and conditions under which code is executed. This is essential for writing logical, efficient, and robust JavaScript programs.
Loops and Iterations
In JavaScript, loops and iterations are fundamental concepts used to repeatedly execute a block of code. This is useful when you need to perform repetitive tasks, such as processing items in an array or running a set of instructions until a specific condition is met.
1. Types of Loops in JavaScript
JavaScript provides several types of loops to iterate over data structures or execute code multiple times:
1.1 for
Loop
The for
loop is the most commonly used loop in JavaScript. It consists of three expressions:
Initialization: Initializes a variable (e.g.,
let i = 0
).Condition: Sets the condition that must be
true
for the loop to continue (e.g.,i < 10
).Update: Updates the loop variable after each iteration (e.g.,
i++
).
Syntax:
for (initialization; condition; update) {
// code block to be executed
}
Example:
for (let i = 0; i < 5; i++) {
console.log(i); // Output: 0, 1, 2, 3, 4
}
Flow:
The initialization step (
let i = 0
) is executed first.Then, the condition (
i < 5
) is evaluated; iftrue
, the code block is executed.After the block is executed, the update step (
i++
) increments the variable.The loop repeats until the condition becomes
false
.
1.2 while
Loop
The while
loop repeats a block of code as long as the specified condition evaluates to true
.
Syntax:
while (condition) {
// code block to be executed
}
Example:
let i = 0;
while (i < 5) {
console.log(i); // Output: 0, 1, 2, 3, 4
i++;
}
Flow:
First, the condition (
i < 5
) is checked.If it’s
true
, the code block is executed.After the block is executed, control goes back to check the condition again.
The loop continues until the condition becomes
false
.
1.3 do-while
Loop
The do-while
loop is similar to the while
loop, except that the code block is executed at least once, even if the condition is false
at the beginning.
Syntax:
do {
// code block to be executed
} while (condition)
Example:
let i = 0;
do {
console.log(i); // Output: 0, 1, 2, 3, 4
i++;
} while (i < 5);
Flow:
The code block is executed before the condition is checked.
Then the condition (
i < 5
) is evaluated. Iftrue
, the loop continues; otherwise, it stops.
1.4 for-in
Loop
The for-in
loop is used to iterate over the enumerable properties (keys) of an object. It is often used for objects rather than arrays.
Syntax:
for (key in object) {
// code block to be executed
}
Example:
let person = {name: "John", age: 25, city: "New York"};
for (let key in person) {
console.log(key + ": " + person[key]); // Output: "name: John", "age: 25", "city: New York"
}
Flow:
The loop iterates over all enumerable properties in the object (
person
), assigning the property name (key) to the variable (key
).For each iteration, you can access the value using
object[key]
.
Note: It's generally not used for arrays because it iterates over all properties, including non-numeric ones, which can lead to unexpected results.
1.5 for-of
Loop
The for-of
loop is used to iterate over iterable objects, such as arrays, strings, maps, sets, and other collections.
Syntax:
for (variable of iterable) {
// code block to be executed
}
Example:
let numbers = [10, 20, 30];
for (let number of numbers) {
console.log(number); // Output: 10, 20, 30
}
Flow:
- The loop iterates over the values of the iterable (e.g., array) and assigns each value to the variable (
number
) in each iteration.
- The loop iterates over the values of the iterable (e.g., array) and assigns each value to the variable (
Note: The for-of
loop is generally preferred over for-in
for arrays.
2. Loop Control Statements
Sometimes, you might want to modify the normal flow of loops. JavaScript provides control statements like break
and continue
to manage this.
2.1 break
Statement
The break
statement is used to exit (terminate) a loop prematurely, even if the loop’s condition hasn’t been met.
Example:
for (let i = 0; i < 5; i++) {
if (i === 3) {
break; // Loop stops when i is 3
}
console.log(i); // Output: 0, 1, 2
Flow:
- When the condition (
i === 3
) is met, thebreak
statement terminates the loop.
- When the condition (
2.2 continue
Statement
The continue
statement skips the current iteration and moves to the next iteration of the loop.
Example:
for (let i = 0; i < 5; i++) {
if (i === 2) {
continue; // Skip the iteration when i is 2
}
console.log(i); // Output: 0, 1, 3, 4
}
Flow:
- When the condition (
i === 2
) is met, thecontinue
statement skips the rest of the code for that iteration and moves to the next iteration.
- When the condition (
3. Iteration with Arrays and Objects
3.1 Iterating Over Arrays
for
Loop: A common approach to iterate over arrays.let arr = [1, 2, 3, 4]; for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }
for-of
Loop: Modern and simpler to use thanfor-in
orfor
.let arr = [1, 2, 3, 4]; for (let value of arr) { console.log(value); }
Array Methods: Functions like
forEach()
,map()
,filter()
, etc., are also used for iteration.let arr = [1, 2, 3, 4]; arr.forEach(value => console.log(value)); // Output: 1, 2, 3, 4
3.2 Iterating Over Objects
While objects are not iterable like arrays, you can use for-in
or methods like Object.keys()
or Object.entries()
to iterate over them.
for-in
Loop:let obj = {name: "Alice", age: 25}; for (let key in obj) { console.log(key, obj[key]); }
Object.keys()
,Object.values()
, andObject.entries()
:let obj = {name: "Alice", age: 25}; Object.keys(obj).forEach(key => console.log(key, obj[key])); // Output: "name Alice", "age 25"
Conclusion
Loops in JavaScript provide mechanisms to repeat actions until a certain condition is met or a specific task is completed.
Different loop types like
for
,while
,do-while
,for-in
, andfor-of
are used in different situations, depending on the data structure and the use case.Control statements like
break
andcontinue
can modify the flow of loops by terminating or skipping iterations.