Welcome to JavaScript
JavaScript is a versatile, high-level programming language primarily used for web development. Created in 1995, it has evolved from a simple scripting language to a powerful tool that runs both in browsers and on servers (Node.js). JavaScript's dynamic typing, first-class functions, and event-driven architecture make it ideal for creating interactive web applications. With the advent of modern frameworks like React, Vue, and Angular, JavaScript has become essential for front-end development, while Node.js enables full-stack JavaScript development. Its ubiquity across web platforms and growing ecosystem make JavaScript one of the most valuable skills for modern developers.
Introduction to JavaScript
How to run JavaScript and write your first "Hello, World!" program.
JavaScript is one of the core technologies of the web, running in browsers and increasingly on servers via Node.js. Getting started with JavaScript is straightforward.
Step 1: Running JavaScript in a Browser
-
Create an HTML File
Create a file namedindex.html
with the following content:<!DOCTYPE html> <html> <head> <title>My First JavaScript</title> </head> <body> <script> // Your JavaScript code goes here console.log("Hello, World!"); </script> </body> </html>
-
Open in Browser
Open the HTML file in your web browser and check the browser's developer console to see the output.
Step 2: Using Node.js
-
Install Node.js
Download and install Node.js from nodejs.org. -
Create a JavaScript File
Create a file namedhello.js
with the following content:console.log("Hello, World!");
-
Run the File
Open a terminal and run:node hello.js
Step 3: Using Browser Developer Tools
Most browsers allow you to run JavaScript directly in the console:
- Right-click on any webpage and select "Inspect" or "Inspect Element"
- Navigate to the "Console" tab
- Type
console.log("Hello, World!");
and press Enter
Congratulations! You've successfully run your first JavaScript code. 🎉
JavaScript Syntax Basics
1. Statements and Semicolons
JavaScript statements are instructions that are executed by the browser or Node.js. While semicolons are optional in many cases due to Automatic Semicolon Insertion (ASI), it's good practice to include them.
Correct Examples:
console.log("Hello"); // Statement with semicolon
let x = 5; // Variable declaration
2. Comments in JavaScript
Comments help developers understand code by adding explanations.
Single-Line Comment:
// This is a single-line comment
console.log("Hello"); // Inline comment
Multi-Line Comment:
/*
This is a multi-line comment.
It can span multiple lines.
*/
console.log("Hello");
3. Case Sensitivity
JavaScript is case-sensitive, meaning it treats uppercase and lowercase letters as different.
Example:
let myVar = 5;
let MyVar = 10;
console.log(myVar); // Outputs: 5
console.log(MyVar); // Outputs: 10 (different variable)
4. Variables and Dynamic Typing
JavaScript is dynamically typed, meaning you don't need to declare variable types explicitly.
Example:
let x = 10; // Number
let y = 3.14; // Number
let z = "Hello"; // String
console.log(typeof x); // Outputs: number
console.log(typeof y); // Outputs: number
console.log(typeof z); // Outputs: string
Reassigning Different Data Types:
let a = 5; // Initially a number
a = "JavaScript"; // Now reassigned as a string
console.log(a); // Outputs: JavaScript
Conclusion
Understanding JavaScript's basic syntax is essential for writing clean code. Key takeaways:
- Use semicolons to end statements (recommended)
- Comments can be single-line (
//
) or multi-line (/* */
) - JavaScript is case-sensitive
- JavaScript supports dynamic typing
console.log()
The console.log()
method in JavaScript is used to print output to the console. It's essential for debugging and monitoring your code.
1. Basic console.log() Usage
The simplest way to use console.log()
:
console.log("Hello, World!"); // Outputs: Hello, World!
console.log(42); // Outputs: 42
You can log multiple values by separating them with commas:
console.log("Hello", "JavaScript"); // Outputs: Hello JavaScript
2. Logging Variables
You can log variables and expressions:
let name = "Alice";
let age = 25;
console.log("Name:", name, "Age:", age);
// Outputs: Name: Alice Age: 25
3. Template Literals for Cleaner Output
Use template literals for more readable output:
console.log(`Name: ${name}, Age: ${age}`);
// Outputs: Name: Alice, Age: 25
4. Other Console Methods
JavaScript provides additional console methods for different purposes:
console.warn("This is a warning"); // Yellow warning
console.error("This is an error"); // Red error
console.info("This is information"); // Blue info
Conclusion
The console.log()
method is fundamental for JavaScript development. Key points:
- Use
console.log(value1, value2, …)
to log multiple values - Template literals provide cleaner string formatting
- Other console methods (
warn
,error
,info
) help categorize output
Arithmetic Operators in JavaScript
Arithmetic operators perform mathematical calculations between two operands.
Examples:
let a = 15;
let b = 4;
console.log(a + b); // 19 (addition)
console.log(a - b); // 11 (subtraction)
console.log(a * b); // 60 (multiplication)
console.log(a / b); // 3.75 (division)
console.log(a % b); // 3 (modulus - remainder)
console.log(a ** b); // 50625 (exponentiation - ES6)
Increment and Decrement:
let x = 5;
x++; // Post-increment: x becomes 6
console.log(x);
let y = 10;
--y; // Pre-decrement: y becomes 9
console.log(y);
Arithmetic operators return a new value without changing the original variables (unless using assignment operators).
Comparison Operators in JavaScript
Comparison operators compare two values and return a boolean result (true
or false
). These include equality, inequality, and relational operators.
Equality Operators
let x = 7;
let y = 10;
console.log(x == y); // false (loose equality)
console.log(x != y); // true (loose inequality)
console.log(x === y); // false (strict equality)
console.log(x !== y); // true (strict inequality)
Relational Operators
console.log(x > y); // false
console.log(x < y); // true
console.log(x >= 7); // true
console.log(y <= 7); // false
Strict vs Loose Equality
JavaScript has two types of equality comparison:
console.log(5 == "5"); // true (loose - type coercion)
console.log(5 === "5"); // false (strict - no type coercion)
console.log(0 == false); // true
console.log(0 === false); // false
console.log(null == undefined); // true
console.log(null === undefined); // false
String Comparison
console.log("apple" < "banana"); // true (lexicographical order)
console.log("2" > "12"); // true (string comparison, not numeric)
Common Pitfalls
- Always use strict equality (
===
and!==
) to avoid unexpected type coercion - Comparing objects compares references, not content
NaN
is not equal to anything, including itself:NaN === NaN
returnsfalse
Logical Operators in JavaScript
Logical operators combine boolean values and return true
or false
. JavaScript has three logical operators: &&
(AND), ||
(OR), and !
(NOT).
Basic Logical Operations
let isSunny = true;
let isWarm = false;
console.log(isSunny && isWarm); // false (both must be true)
console.log(isSunny || isWarm); // true (at least one is true)
console.log(!isWarm); // true (negates the value)
Truthy and Falsy Values
JavaScript uses type coercion with logical operators. Falsy values: false
, 0
, ""
, null
, undefined
, NaN
. Everything else is truthy.
console.log(0 && "hello"); // 0 (first falsy value)
console.log("hello" && "world"); // "world" (last truthy value)
console.log(null || "default"); // "default" (first truthy value)
console.log(!!"hello"); // true (double negation for boolean conversion)
Short-Circuit Evaluation
Logical operators short-circuit - they stop evaluating as soon as the result is determined.
// && stops at first falsy value
let result1 = false && someFunction(); // someFunction never called
// || stops at first truthy value
let result2 = true || someFunction(); // someFunction never called
// Practical examples
let user = null;
let username = user || "Guest"; // "Guest" (default value)
let config = { timeout: 5000 };
let timeout = config.timeout && config.timeout > 0 ? config.timeout : 3000;
Logical Operators with Non-Booleans
// Using && for conditional execution
let obj = { name: "John" };
obj && console.log(obj.name); // Only logs if obj exists
// Using || for fallback values
let input = "";
let value = input || "default value"; // "default value"
Common Pitfalls
- Remember that logical operators return the actual value, not necessarily a boolean
&&
has higher precedence than||
- Be careful with operator precedence: use parentheses when in doubt
Bitwise Operators in JavaScript
Bitwise operators treat their operands as 32-bit binary numbers and perform operations at the bit level. They convert numbers to 32-bit integers, perform the operation, then convert back.
Basic Bitwise Operations
let a = 6; // binary: 00000000000000000000000000000110
let b = 3; // binary: 00000000000000000000000000000011
console.log(a & b); // 2 (binary: 00000010) - AND
console.log(a | b); // 7 (binary: 00000111) - OR
console.log(a ^ b); // 5 (binary: 00000101) - XOR
console.log(~a); // -7 (binary: 11111001) - NOT (ones' complement)
Bitwise Shift Operators
let num = 8; // binary: 00000000000000000000000000001000
console.log(num << 1); // 16 (binary: 00010000) - left shift
console.log(num >> 1); // 4 (binary: 00000100) - right shift (sign-propagating)
console.log(num >>> 1); // 4 (binary: 00000100) - right shift (zero-fill)
// Negative numbers
let negative = -8;
console.log(negative >> 1); // -4 (sign preserved)
console.log(negative >>> 1); // 2147483644 (zero filled)
Practical Applications
// Checking if a number is even
function isEven(n) {
return (n & 1) === 0;
}
console.log(isEven(4)); // true
console.log(isEven(7)); // false
// Swapping two numbers without temporary variable
let x = 5, y = 10;
x = x ^ y;
y = x ^ y;
x = x ^ y;
console.log(x, y); // 10, 5
// Setting and checking flags
const READ = 1; // 0001
const WRITE = 2; // 0010
const EXECUTE = 4; // 0100
let permissions = READ | WRITE; // 0011 (has read and write)
console.log(permissions & READ); // 1 (true - has read permission)
console.log(permissions & EXECUTE); // 0 (false - no execute permission)
Bit Manipulation Examples
// Getting the nth bit
function getBit(num, position) {
return (num >> position) & 1;
}
console.log(getBit(5, 0)); // 1 (5 is 101 in binary)
// Setting the nth bit
function setBit(num, position) {
return num | (1 << position);
}
console.log(setBit(5, 1)); // 7 (101 becomes 111)
// Clearing the nth bit
function clearBit(num, position) {
return num & ~(1 << position);
}
console.log(clearBit(5, 0)); // 4 (101 becomes 100)
Common Pitfalls
- Bitwise operators convert numbers to 32-bit signed integers
- The result of
~
(NOT) may be unexpected due to two's complement representation >>>
(zero-fill right shift) treats numbers as unsigned- Bitwise operations on large numbers may produce unexpected results due to 32-bit limitation
Assignment Operators in JavaScript
Assignment operators assign values to variables. Compound assignment operators combine assignment with arithmetic or bitwise operations.
Basic Assignment
let x = 10; // simple assignment
let y = x; // assign value of x to y
console.log(x, y); // 10, 10
Compound Assignment Operators
let a = 10;
a += 5; // equivalent to a = a + 5 → 15
console.log(a);
a -= 3; // equivalent to a = a - 3 → 12
console.log(a);
a *= 2; // equivalent to a = a * 2 → 24
console.log(a);
a /= 4; // equivalent to a = a / 4 → 6
console.log(a);
a %= 4; // equivalent to a = a % 4 → 2
console.log(a);
a **= 3; // equivalent to a = a ** 3 → 8 (exponentiation)
console.log(a);
Bitwise Assignment Operators
let b = 5; // binary: 101
b &= 3; // b = b & 3 → 1 (101 & 011 = 001)
console.log(b);
b |= 2; // b = b | 2 → 3 (001 | 010 = 011)
console.log(b);
b ^= 1; // b = b ^ 1 → 2 (011 ^ 001 = 010)
console.log(b);
b <<= 1; // b = b </lt; 1 → 4 (010 becomes 100)
console.log(b);
b >>= 1; // b = b >> 1 → 2 (100 becomes 010)
console.log(b);
Chained Assignment
let p, q, r;
p = q = r = 10; // all variables get value 10
console.log(p, q, r); // 10, 10, 10
Destructuring Assignment
ES6 introduced destructuring assignment for arrays and objects:
// Array destructuring
let [first, second, third] = [1, 2, 3];
console.log(first, second, third); // 1, 2, 3
// Object destructuring
let person = { name: "Alice", age: 30 };
let { name, age } = person;
console.log(name, age); // Alice, 30
// Swapping variables using destructuring
let m = 5, n = 10;
[m, n] = [n, m];
console.log(m, n); // 10, 5
Assignment with Logical Operators
// Logical OR assignment (||=) - ES2021
let username = "";
username ||= "Guest"; // assigns "Guest" only if username is falsy
console.log(username); // "Guest"
// Logical AND assignment (&&=) - ES2021
let user = { name: "John" };
user.name &&= user.name.toUpperCase(); // modifies only if name exists
console.log(user.name); // "JOHN"
// Nullish coalescing assignment (??=) - ES2021
let config = {};
config.timeout ??= 5000; // assigns only if timeout is null or undefined
console.log(config.timeout); // 5000
Common Pitfalls
- Using
=
(assignment) instead of===
(comparison) in conditions - Chained assignment creates shared references for objects, not copies
- Compound assignment operators have lower precedence than regular arithmetic operators
- Destructuring requires matching patterns on both sides
Numbers in JavaScript
JavaScript has one numeric type: Number
. It represents both integers and floating-point numbers.
1. Creating Numbers
Numbers can be created by assigning numeric values to variables:
let a = 10;
let b = -5;
let c = 3.14;
let d = 2.998e8; // Scientific notation: 2.998 × 10^8
console.log(a, b, c, d);
2. Number Operations
Numbers support various arithmetic operations:
console.log(5 + 3); // Addition
console.log(5 - 3); // Subtraction
console.log(5 * 3); // Multiplication
console.log(5 / 3); // Division
console.log(5 % 3); // Modulus
console.log(5 ** 3); // Exponentiation
3. Special Numeric Values
console.log(Infinity); // Positive infinity
console.log(-Infinity); // Negative infinity
console.log(NaN); // Not a Number
4. Type Conversion
Converting other types to numbers:
let num = Number("123");
console.log(num); // Outputs: 123
let floatNum = parseFloat("3.1415");
console.log(floatNum); // Outputs: 3.1415
let intNum = parseInt("42", 10);
console.log(intNum); // Outputs: 42
Arrays in JavaScript
Arrays are used to store multiple values in a single variable. They are dynamic and can hold mixed data types.
1. Creating Arrays
Define arrays with various elements:
// Array literal syntax
let fruits = ["apple", "banana", "orange"];
let mixed = [1, "hello", true, null];
// Using Array constructor
let numbers = new Array(1, 2, 3);
console.log(fruits);
console.log(mixed);
2. Accessing and Modifying Arrays
Arrays support indexing and various methods for modification:
// Accessing elements
console.log(fruits[0]); // Outputs: apple
// Changing elements
fruits[1] = "blueberry";
console.log(fruits);
// Adding elements
fruits.push("grape"); // Add to end
fruits.unshift("mango"); // Add to beginning
console.log(fruits);
3. Array Length
console.log(fruits.length); // Number of elements
Variables in JavaScript
Variables are used to store data in JavaScript. Modern JavaScript provides three ways to declare variables: var
, let
, and const
.
1. Variable Declaration
Different ways to declare variables:
var oldWay = "function scoped"; // Function scope (avoid in modern JS)
let modernWay = "block scoped"; // Block scope - can be reassigned
const constantValue = "cannot change"; // Block scope - cannot be reassigned
2. Variable Naming Rules
- Must start with a letter, underscore (_), or dollar sign ($)
- Cannot start with a number
- Are case-sensitive
- Cannot use reserved keywords
3. Assigning Multiple Variables
let a = 4, b = 5, c = 6;
console.log(a, b, c); // Outputs: 4 5 6
// Destructuring assignment
let [x, y] = [10, 20];
console.log(x, y); // Outputs: 10 20
4. Variable Hoisting
var
declarations are hoisted to the top of their scope, while let
and const
are not:
console.log(hoistedVar); // undefined (due to hoisting)
var hoistedVar = "I'm hoisted";
// This would cause an error:
// console.log(notHoisted); // ReferenceError
// let notHoisted = "I'm not hoisted";
Strings in JavaScript
Strings are sequences of characters used to represent text. They are immutable and can be defined using single quotes, double quotes, or backticks for template literals.
1. Creating Strings
Strings can be created using different types of quotes:
let singleQuotes = 'Hello, World!';
let doubleQuotes = "JavaScript is great";
let backticks = `This is a template literal`;
let multiLine = `This is a
multi-line
string`;
console.log(singleQuotes, doubleQuotes, backticks);
2. String Operations
Strings support various operations like concatenation and repetition:
let str1 = "Hello";
let str2 = "World";
console.log(str1 + " " + str2); // Concatenation: "Hello World"
console.log(str1.repeat(3)); // Repetition: "HelloHelloHello"
console.log(str1[0]); // Accessing characters: "H"
console.log(str1.charAt(0)); // Accessing characters: "H"
console.log(str1.length); // String length: 5
3. Template Literals (ES6)
Template literals allow embedded expressions and multi-line strings:
let name = "Alice";
let age = 25;
// String interpolation
let greeting = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(greeting);
// Multi-line without escape characters
let message = `This is line one
This is line two
This is line three`;
console.log(message);
// Expressions inside template literals
let calculation = `The sum of 5 and 3 is ${5 + 3}`;
console.log(calculation);
4. String Immutability
Strings are immutable - they cannot be changed once created:
let text = "Hello";
text[0] = "J"; // This won't work
console.log(text); // Still "Hello"
// To "change" a string, create a new one
text = "J" + text.slice(1);
console.log(text); // "Jello"
String Methods in JavaScript
JavaScript provides many built-in methods for string manipulation including trimming, splitting, replacing, and case conversion.
Trimming with .trim()
let text = " hello world ";
console.log(text.trim()); // "hello world"
console.log(text.trimStart()); // "hello world "
console.log(text.trimEnd()); // " hello world"
Splitting Strings
let line = "apple,banana,cherry";
console.log(line.split(",")); // ['apple', 'banana', 'cherry']
let words = "one two three".split(" ");
console.log(words); // ['one', 'two', 'three']
// Split with limit
console.log("a,b,c,d".split(",", 2)); // ['a', 'b']
Replacing Text
let sentence = "I like cats";
console.log(sentence.replace("cats", "dogs")); // "I like dogs"
// Global replace with regex
let text = "apple apple apple";
console.log(text.replace(/apple/g, "orange")); // "orange orange orange"
// Replace with function
console.log("hello world".replace(/o/g, match => match.toUpperCase())); // "hellO wOrld"
Case Conversion
let mixedCase = "Hello World";
console.log(mixedCase.toUpperCase()); // "HELLO WORLD"
console.log(mixedCase.toLowerCase()); // "hello world"
// Capitalize first letter
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
console.log(capitalize("hello")); // "Hello"
Searching in Strings
let text = "JavaScript is awesome";
console.log(text.indexOf("Script")); // 4
console.log(text.includes("awesome")); // true
console.log(text.startsWith("Java")); // true
console.log(text.endsWith("some")); // true
// Using regex for search
console.log(text.search(/[A-Z]/)); // 0 (position of first capital letter)
Common Pitfalls
.replace()
only replaces the first occurrence by default - use regex with/g
flag for global replace.split()
on an empty string returns[""]
, not an empty array- String methods return new strings - they don't modify the original
.indexOf()
returns -1 when the substring is not found
String Formatting & Advanced Methods
JavaScript offers several ways to format strings including template literals, padding, and extracting substrings.
Template Literals for Formatting
let name = "Alice";
let score = 95.5;
// Basic interpolation
console.log(`Player: ${name}, Score: ${score}`);
// Expressions and function calls
console.log(`Next level: ${Math.floor(score / 10) + 1}`);
// Conditional formatting
console.log(`Status: ${score >= 90 ? 'Excellent' : 'Good'}`);
Padding Strings
let number = "5";
console.log(number.padStart(3, "0")); // "005"
console.log(number.padEnd(3, "0")); // "500"
// Formatting numbers
let hours = "3", minutes = "7";
console.log(`${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`); // "03:07"
Substring Extraction
let text = "Hello World";
console.log(text.slice(0, 5)); // "Hello" (start to end-1)
console.log(text.substring(0, 5)); // "Hello" (start to end-1)
console.log(text.substr(6, 5)); // "World" (start, length)
// Negative indices with slice
console.log(text.slice(-5)); // "World" (last 5 characters)
console.log(text.slice(6, -1)); // "Worl" (from index 6 to second-last)
Character Access
let str = "Hello";
console.log(str.charAt(1)); // "e"
console.log(str[1]); // "e" (array-like access)
console.log(str.charCodeAt(1)); // 101 (Unicode code point)
// Converting from character code
console.log(String.fromCharCode(72, 101, 108, 108, 111)); // "Hello"
String Comparison
console.log("a".localeCompare("b")); // -1 (a comes before b)
console.log("b".localeCompare("a")); // 1 (b comes after a)
console.log("a".localeCompare("a")); // 0 (equal)
// Case-insensitive comparison
console.log("Hello".toLowerCase() === "hello".toLowerCase()); // true
// Using Intl.Collator for advanced sorting
const collator = new Intl.Collator('en');
console.log(collator.compare("apple", "Banana")); // -1 (apple before Banana)
Arrays in JavaScript
Arrays are used to store multiple values in a single variable. They are dynamic, can hold mixed data types, and provide many built-in methods for manipulation.
1. Creating Arrays
Arrays can be created using different syntax:
// Array literal (recommended)
let fruits = ["apple", "banana", "orange"];
let mixed = [1, "hello", true, null, {name: "John"}];
// Array constructor
let numbers = new Array(1, 2, 3);
let empty = new Array(5); // Creates array with 5 empty slots
// Array.of() - better for single numeric arguments
let single = Array.of(5); // [5], not array with 5 empty slots
console.log(fruits, mixed, numbers, empty, single);
2. Array Properties
let arr = ["a", "b", "c"];
console.log(arr.length); // 3 (number of elements)
console.log(Array.isArray(arr)); // true (check if is array)
// Arrays are objects
console.log(typeof arr); // "object"
3. Sparse Arrays
let sparse = ["a", , "c"]; // Hole at index 1
console.log(sparse.length); // 3
console.log(sparse[1]); // undefined
console.log(1 in sparse); // false (hole exists)
// vs dense array
let dense = ["a", undefined, "c"];
console.log(1 in dense); // true (property exists)
Array Indexing in JavaScript
Arrays are zero-indexed, meaning the first element is at index 0. You can access and modify elements using bracket notation.
Basic Indexing
let fruits = ["apple", "banana", "cherry"];
console.log(fruits[0]); // "apple" (first element)
console.log(fruits[1]); // "banana" (second element)
console.log(fruits[-1]); // undefined (no negative indexing)
console.log(fruits[5]); // undefined (out of bounds)
Modifying Elements
let numbers = [10, 20, 30];
numbers[1] = 25; // Modify existing element
console.log(numbers); // [10, 25, 30]
numbers[3] = 40; // Add new element
console.log(numbers); // [10, 25, 30, 40]
// Adding with large index creates sparse array
numbers[10] = 100;
console.log(numbers); // [10, 25, 30, 40, empty × 6, 100]
console.log(numbers.length); // 11
Array Length Property
let arr = [1, 2, 3];
console.log(arr.length); // 3
// Modifying length
arr.length = 5;
console.log(arr); // [1, 2, 3, empty × 2]
arr.length = 2;
console.log(arr); // [1, 2] (truncated)
// Empty array
arr.length = 0;
console.log(arr); // []
Finding Elements
let data = [10, 20, 30, 20, 40];
console.log(data.indexOf(20)); // 1 (first occurrence)
console.log(data.lastIndexOf(20)); // 3 (last occurrence)
console.log(data.indexOf(50)); // -1 (not found)
// Finding with condition
console.log(data.find(num => num > 25)); // 30 (first match)
console.log(data.findIndex(num => num > 25)); // 2 (index of first match)
Common Pitfalls
- JavaScript arrays don't have negative indexing like Python
- Accessing out-of-bounds indices returns
undefined
, not an error - The
length
property is mutable and can truncate or extend arrays - Sparse arrays can cause unexpected behavior with some array methods
Array Methods in JavaScript
JavaScript arrays have many built-in methods for manipulation, iteration, and transformation.
Adding/Removing Elements
let fruits = ["apple", "banana"];
// Add to end
fruits.push("orange"); // returns new length: 3
console.log(fruits); // ["apple", "banana", "orange"]
// Remove from end
let last = fruits.pop(); // returns "orange"
console.log(fruits); // ["apple", "banana"]
// Add to beginning
fruits.unshift("strawberry"); // returns new length: 3
console.log(fruits); // ["strawberry", "apple", "banana"]
// Remove from beginning
let first = fruits.shift(); // returns "strawberry"
console.log(fruits); // ["apple", "banana"]
Splicing Arrays
let colors = ["red", "green", "blue", "yellow"];
// Remove elements
let removed = colors.splice(1, 2); // remove 2 elements from index 1
console.log(removed); // ["green", "blue"]
console.log(colors); // ["red", "yellow"]
// Add elements
colors.splice(1, 0, "purple", "pink"); // add at index 1, remove 0
console.log(colors); // ["red", "purple", "pink", "yellow"]
// Replace elements
colors.splice(1, 2, "orange", "cyan"); // replace 2 elements at index 1
console.log(colors); // ["red", "orange", "cyan", "yellow"]
Transformation Methods
let numbers = [1, 2, 3, 4];
// map - transform each element
let doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
// filter - keep elements that pass test
let evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
// reduce - accumulate values
let sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 10
Searching and Sorting
let data = [40, 10, 30, 20];
// Sorting
data.sort(); // [10, 20, 30, 40] (lexicographical by default)
console.log(data);
// Numeric sort
data.sort((a, b) => a - b); // [10, 20, 30, 40]
console.log(data);
// Reverse
data.reverse();
console.log(data); // [40, 30, 20, 10]
// Some and Every
console.log(data.some(num => num > 25)); // true (at least one)
console.log(data.every(num => num > 5)); // true (all)
Iteration Methods
let items = ["a", "b", "c"];
// forEach - execute function for each element
items.forEach((item, index) => {
console.log(`${index}: ${item}`);
});
// Using entries() for index-value pairs
for (let [index, value] of items.entries()) {
console.log(index, value);
}
Objects in JavaScript
Objects are collections of key-value pairs used to store structured data. They are fundamental to JavaScript and used for everything from simple data storage to complex functionality.
1. Creating Objects
// Object literal (most common)
let person = {
name: "Alice",
age: 30,
isStudent: false
};
// Using Object constructor
let car = new Object();
car.make = "Toyota";
car.model = "Camry";
// Computed property names (ES6)
let key = "status";
let dynamicObj = {
[key]: "active",
["prop" + "Name"]: "value"
};
console.log(person, car, dynamicObj);
2. Accessing Properties
let book = {
title: "JavaScript Guide",
author: "John Doe",
year: 2023
};
// Dot notation
console.log(book.title); // "JavaScript Guide"
// Bracket notation
console.log(book["author"]); // "John Doe"
// Dynamic property access
let property = "year";
console.log(book[property]); // 2023
// Non-existent properties
console.log(book.publisher); // undefined
3. Modifying Objects
let user = { name: "Bob" };
// Adding properties
user.age = 25;
user["email"] = "bob@example.com";
// Modifying properties
user.name = "Robert";
// Deleting properties
delete user.email;
console.log(user); // { name: "Robert", age: 25 }
4. Object References
let obj1 = { value: 10 };
let obj2 = obj1; // Reference, not copy
obj2.value = 20;
console.log(obj1.value); // 20 (both reference same object)
// Creating a shallow copy
let obj3 = { ...obj1 }; // Spread operator (ES6)
obj3.value = 30;
console.log(obj1.value); // 20 (unchanged)
Object Methods and Operations
JavaScript provides many built-in methods for working with objects, including iteration, property management, and object creation.
Object Static Methods
let person = { name: "Alice", age: 30, city: "New York" };
// Get all keys
console.log(Object.keys(person)); // ["name", "age", "city"]
// Get all values
console.log(Object.values(person)); // ["Alice", 30, "New York"]
// Get key-value pairs
console.log(Object.entries(person)); // [["name", "Alice"], ["age", 30], ["city", "New York"]]
// Check if property exists
console.log(person.hasOwnProperty("name")); // true
console.log(Object.hasOwn(person, "age")); // true (newer method)
Iterating Over Objects
let data = { a: 1, b: 2, c: 3 };
// Using for...in loop
for (let key in data) {
if (data.hasOwnProperty(key)) {
console.log(`${key}: ${data[key]}`);
}
}
// Using Object methods
Object.keys(data).forEach(key => {
console.log(`${key}: ${data[key]}`);
});
// Using entries with for...of
for (let [key, value] of Object.entries(data)) {
console.log(key, value);
}
Merging and Copying Objects
let obj1 = { a: 1, b: 2 };
let obj2 = { b: 3, c: 4 };
// Object.assign (shallow merge)
let merged = Object.assign({}, obj1, obj2);
console.log(merged); // { a: 1, b: 3, c: 4 }
// Spread operator (ES6 - preferred)
let spread = { ...obj1, ...obj2 };
console.log(spread); // { a: 1, b: 3, c: 4 }
// Deep cloning (for nested objects)
let nested = { a: 1, b: { c: 2 } };
let deepClone = JSON.parse(JSON.stringify(nested));
Property Descriptors
let obj = {};
// Define property with descriptors
Object.defineProperty(obj, 'readOnly', {
value: 42,
writable: false,
enumerable: true,
configurable: false
});
console.log(obj.readOnly); // 42
obj.readOnly = 100; // Fails silently in non-strict mode
console.log(obj.readOnly); // Still 42
// Get property descriptors
console.log(Object.getOwnPropertyDescriptor(obj, 'readOnly'));
Object Creation Patterns
// Factory function
function createUser(name, age) {
return {
name,
age,
greet() {
return `Hello, I'm ${this.name}`;
}
};
}
// Constructor function
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return `Hello, I'm ${this.name}`;
};
// Class syntax (ES6)
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
Booleans in JavaScript
Boolean is a primitive data type that can have one of two values: true
or false
. JavaScript uses truthy and falsy values in boolean contexts.
Boolean Values
let isActive = true;
let isComplete = false;
console.log(typeof isActive); // "boolean"
console.log(Boolean(isActive)); // true
Falsy Values
These values evaluate to false
in boolean contexts:
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
Truthy Values
Everything else is truthy:
console.log(Boolean(true)); // true
console.log(Boolean(1)); // true
console.log(Boolean(-1)); // true
console.log(Boolean("hello")); // true
console.log(Boolean("false")); // true (string "false" is truthy!)
console.log(Boolean([])); // true
console.log(Boolean({})); // true
console.log(Boolean(function() {})); // true
Boolean Conversion
// Double negation for quick boolean conversion
console.log(!!"hello"); // true
console.log(!!0); // false
console.log(!!null); // false
// In conditional contexts
let name = "";
if (name) {
console.log("Name exists");
} else {
console.log("No name provided"); // This runs
}
// With logical operators
let value = "hello";
let result = value || "default"; // "hello" (first truthy)
console.log(result);
null and undefined in JavaScript
null
and undefined
represent the absence of value, but they are used in different contexts.
undefined
undefined
means a variable has been declared but not assigned a value:
let unassigned;
console.log(unassigned); // undefined
let obj = {};
console.log(obj.nonExistent); // undefined
function noReturn() {}
console.log(noReturn()); // undefined
// Explicit undefined
let value = undefined;
console.log(value);
null
null
is an assignment value that represents intentional absence of any object value:
let empty = null;
console.log(empty); // null
// Commonly used to clear object references
let data = { name: "John" };
data = null; // Clear the reference
Comparison
console.log(null == undefined); // true (loose equality)
console.log(null === undefined); // false (strict equality)
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (historical bug)
// Checking for null or undefined
let value = null;
if (value === null || value === undefined) {
console.log("Value is null or undefined");
}
// Using nullish coalescing (ES2020)
let result = value ?? "default"; // "default"
console.log(result);
Practical Usage
// Function parameters
function greet(name = "Guest") { // Default parameter
console.log(`Hello, ${name}`);
}
greet(); // "Hello, Guest"
greet(undefined); // "Hello, Guest"
greet(null); // "Hello, null"
// Object destructuring with defaults
let user = { name: null };
let { name = "Anonymous" } = user;
console.log(name); // null (not "Anonymous" - null is explicit)
// Nullish coalescing operator
let settings = { theme: null };
let theme = settings.theme ?? "dark"; // "dark"
console.log(theme);
Conditional Statements: if, else if, and else
Conditional statements allow you to execute different code blocks based on conditions. JavaScript provides if
, else if
, and else
for decision making.
1. Basic if Statement
let temperature = 25;
if (temperature > 30) {
console.log("It's hot outside!");
}
// With curly braces (always recommended)
if (temperature > 20) {
console.log("Nice weather!");
console.log("Perfect for a walk!");
}
2. if-else Statement
let age = 16;
if (age >= 18) {
console.log("You are an adult");
} else {
console.log("You are a minor");
}
3. if-else if-else Chain
let score = 85;
let grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else if (score >= 60) {
grade = "D";
} else {
grade = "F";
}
console.log(`Score: ${score}, Grade: ${grade}`); // Score: 85, Grade: B
4. Nested Conditionals
let isMember = true;
let orderAmount = 120;
if (isMember) {
if (orderAmount > 100) {
console.log("Free shipping and 10% discount!");
} else {
console.log("Free shipping!");
}
} else {
console.log("Standard shipping rates apply");
}
5. Ternary Operator
let isRaining = true;
let action = isRaining ? "Take an umbrella" : "Enjoy the sun";
console.log(action); // "Take an umbrella"
// Nested ternary (use sparingly)
let userRole = "admin";
let accessLevel = userRole === "admin" ? "full" :
userRole === "user" ? "limited" : "none";
console.log(accessLevel); // "full"
6. Truthy/Falsy in Conditions
let name = "";
let count = 0;
let data = null;
if (name) {
console.log("Name provided"); // Won't run (empty string is falsy)
}
if (count) {
console.log("Count is non-zero"); // Won't run (0 is falsy)
}
if (data) {
console.log("Data exists"); // Won't run (null is falsy)
}
// Checking for existence
let user = { name: "John" };
if (user && user.name) {
console.log(`Hello, ${user.name}`); // Runs
}
for Loop in JavaScript
The for
loop repeats a block of code a specific number of times. It's ideal when you know how many iterations you need.
Basic for Loop
// Count from 0 to 4
for (let i = 0; i < 5; i++) {
console.log("Iteration:", i);
}
// Countdown from 5 to 1
for (let i = 5; i > 0; i--) {
console.log("Countdown:", i);
}
Looping Through Arrays
let fruits = ["apple", "banana", "orange"];
// Standard array iteration
for (let i = 0; i < fruits.length; i++) {
console.log(`Fruit ${i}: ${fruits[i]}`);
}
// Backwards iteration
for (let i = fruits.length - 1; i >= 0; i--) {
console.log(fruits[i]);
}
Multiple Variables
// Multiple loop variables
for (let i = 0, j = 10; i < j; i++, j--) {
console.log(`i: ${i}, j: ${j}`);
}
// Fibonacci sequence
let a = 0, b = 1;
for (let i = 0; i < 10; i++) {
console.log(a);
[a, b] = [b, a + b]; // Destructuring assignment
}
Complex Conditions
// Loop until condition is met
for (let num = 1; num <= 20; num++) {
if (num % 3 === 0 && num % 5 === 0) {
console.log("FizzBuzz");
break; // Stop at first FizzBuzz
}
}
// Skipping iterations
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue; // Skip even numbers
}
console.log("Odd number:", i);
}
Performance Considerations
let largeArray = new Array(1000);
// Cache length for better performance
for (let i = 0, len = largeArray.length; i < len; i++) {
// Process array
}
// Pre-decrement in condition (sometimes faster)
for (let i = largeArray.length; i--;) {
console.log(largeArray[i]);
}
while Loop in JavaScript
The while
loop repeats a block of code as long as a condition is true. It's useful when you don't know how many iterations you'll need in advance.
Basic while Loop
let count = 1;
while (count <= 5) {
console.log("Count:", count);
count++; // Don't forget to update the condition!
}
console.log("Loop finished");
Input Validation Example
// Simulating user input
let userInput = "";
let attempts = 0;
while (userInput !== "yes" && attempts < 3) {
// In real code, this would be prompt() or form input
userInput = attempts === 2 ? "yes" : "no"; // Simulate final attempt success
attempts++;
console.log(`Attempt ${attempts}: ${userInput}`);
}
console.log("Input accepted!");
Infinite Loop Protection
let value = 0;
let safetyCounter = 0;
while (value !== 10) {
value = Math.floor(Math.random() * 10) + 1; // Random 1-10
safetyCounter++;
if (safetyCounter > 100) {
console.log("Safety break triggered!");
break;
}
}
console.log(`Found ${value} after ${safetyCounter} attempts`);
do...while Loop
let userResponse;
let tries = 0;
do {
// Code runs at least once
userResponse = tries === 0 ? "no" : "yes"; // Simulate response
tries++;
console.log(`Try ${tries}: ${userResponse}`);
} while (userResponse !== "yes" && tries < 3);
console.log("Completed!");
for...of Loop in JavaScript
The for...of
loop (ES6) iterates over iterable objects like arrays, strings, maps, sets, etc. It provides a cleaner syntax for iterating through values.
Iterating Arrays
let fruits = ["apple", "banana", "orange"];
for (let fruit of fruits) {
console.log(fruit);
}
// Output: apple, banana, orange
Iterating Strings
let text = "Hello";
for (let char of text) {
console.log(char);
}
// Output: H, e, l, l, o
Getting Index with entries()
let colors = ["red", "green", "blue"];
for (let [index, color] of colors.entries()) {
console.log(`Color ${index}: ${color}`);
}
// Output: Color 0: red, Color 1: green, Color 2: blue
Other Iterables
// Set
let uniqueNumbers = new Set([1, 2, 2, 3, 4]);
for (let num of uniqueNumbers) {
console.log(num); // 1, 2, 3, 4 (no duplicates)
}
// Map
let scores = new Map([["Alice", 95], ["Bob", 87]]);
for (let [name, score] of scores) {
console.log(`${name}: ${score}`);
}
// Arguments object
function sumAll() {
let total = 0;
for (let num of arguments) {
total += num;
}
return total;
}
console.log(sumAll(1, 2, 3, 4)); // 10
Comparison with for...in
let array = ["a", "b", "c"];
array.customProp = "I'm custom";
// for...of - values only
for (let value of array) {
console.log(value); // "a", "b", "c"
}
// for...in - keys/properties
for (let key in array) {
console.log(key); // "0", "1", "2", "customProp"
}
for...in Loop in JavaScript
The for...in
loop iterates over the enumerable properties of an object, including inherited ones. It's mainly used for object iteration, not arrays.
Iterating Object Properties
let person = {
name: "Alice",
age: 30,
city: "New York"
};
for (let key in person) {
console.log(`${key}: ${person[key]}`);
}
// Output: name: Alice, age: 30, city: New York
Checking Own Properties
let car = { make: "Toyota", model: "Camry" };
// Add inherited property
Object.prototype.color = "red";
for (let prop in car) {
if (car.hasOwnProperty(prop)) {
console.log("Own property:", prop);
} else {
console.log("Inherited property:", prop);
}
}
Array Iteration (Not Recommended)
let fruits = ["apple", "banana", "orange"];
fruits.customProperty = "I'm not an array element";
for (let index in fruits) {
console.log(`Index ${index}: ${fruits[index]}`);
}
// Output includes: Index customProperty: I'm not an array element
Order of Iteration
let obj = {
3: "three",
1: "one",
2: "two",
b: "b",
a: "a"
};
for (let key in obj) {
console.log(key);
}
// Modern JS: "1", "2", "3", "b", "a" (numeric keys first, in order)
Practical Examples
// Counting properties
function countProperties(obj) {
let count = 0;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
count++;
}
}
return count;
}
// Copying properties
function copyProperties(source, target) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
return target;
}
Loop Control Statements
JavaScript provides break
, continue
, and labels to control loop execution flow.
break Statement
// Breaking out of a loop
for (let i = 0; i < 10; i++) {
if (i === 5) {
break; // Exit loop completely
}
console.log(i); // 0, 1, 2, 3, 4
}
// Breaking from while loop
let num = 0;
while (true) {
if (num > 5) {
break;
}
console.log(num);
num++;
}
continue Statement
// Skipping even numbers
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue; // Skip to next iteration
}
console.log(i); // 1, 3, 5, 7, 9
}
// Skipping specific values
let fruits = ["apple", "banana", null, "orange", undefined];
for (let fruit of fruits) {
if (!fruit) {
continue; // Skip null/undefined
}
console.log(fruit.toUpperCase());
}
Labels with break and continue
// Breaking out of nested loops
outerLoop: for (let i = 0; i < 3; i++) {
innerLoop: for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break outerLoop; // Break out of both loops
}
console.log(`i=${i}, j=${j}`);
}
}
// Continuing outer loop from inner loop
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 1) {
continue outer; // Continue outer loop
}
console.log(`i=${i}, j=${j}`);
}
}
Practical Examples
// Finding first match in nested arrays
let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let found = false;
search: for (let row of matrix) {
for (let value of row) {
if (value === 5) {
console.log("Found 5!");
found = true;
break search;
}
}
}
// Skipping processing for certain conditions
let data = [1, -2, 3, -4, 5];
for (let num of data) {
if (num < 0) {
console.log("Skipping negative number");
continue;
}
console.log(`Processing: ${num}`);
}
Nested Loops
Nested loops are loops inside other loops. They're essential for working with multi-dimensional data, combinations, and complex iterations.
2D Array Iteration
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// Using nested for loops
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(`matrix[${i}][${j}] = ${matrix[i][j]}`);
}
}
// Using nested for...of loops
for (let row of matrix) {
for (let value of row) {
console.log(value);
}
}
Combinations and Permutations
let colors = ["red", "green", "blue"];
let sizes = ["S", "M", "L"];
// Generate all combinations
for (let color of colors) {
for (let size of sizes) {
console.log(`${color} ${size}`);
}
}
// Triangular pattern (avoiding duplicates)
let numbers = [1, 2, 3, 4];
for (let i = 0; i < numbers.length; i++) {
for (let j = i + 1; j < numbers.length; j++) {
console.log(`Pair: ${numbers[i]}, ${numbers[j]}`);
}
}
Nested while Loops
let i = 1;
while (i <= 3) {
let j = 1;
while (j <= 2) {
console.log(`i=${i}, j=${j}`);
j++;
}
i++;
}
Performance Considerations
// Inefficient nested loops
let bigArray = new Array(1000);
let bigArray2 = new Array(1000);
// O(n²) complexity - 1,000,000 iterations!
for (let i = 0; i < bigArray.length; i++) {
for (let j = 0; j < bigArray2.length; j++) {
// Intensive operation
}
}
// Optimized version - cache lengths
for (let i = 0, len1 = bigArray.length; i < len1; i++) {
for (let j = 0, len2 = bigArray2.length; j < len2; j++) {
// Intensive operation
}
}
Practical Example: Multiplication Table
// Generate multiplication table
for (let i = 1; i <= 10; i++) {
let row = "";
for (let j = 1; j <= 10; j++) {
row += `${i * j}\t`; // Tab separated
}
console.log(row);
}
Function Declaration and Expression
Functions are fundamental building blocks in JavaScript. They can be declared in several ways and have access to their own scope and the outer scope.
Function Declaration
// Function declaration (hoisted)
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
Function Expression
// Function expression (not hoisted)
const calculateArea = function(width, height) {
return width * height;
};
console.log(calculateArea(5, 3)); // 15
Parameters and Arguments
// Default parameters (ES6)
function createUser(name, age = 18, isActive = true) {
return { name, age, isActive };
}
console.log(createUser("John")); // {name: "John", age: 18, isActive: true}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
Return Values
// Multiple returns
function getGrade(score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
return "F";
}
// Returning objects
function createPerson(name, age) {
return {
name,
age,
isAdult: age >= 18
};
}
Function Scope
let globalVar = "I'm global";
function demonstrateScope() {
let localVar = "I'm local";
console.log(globalVar); // Accessible
console.log(localVar); // Accessible
function innerFunction() {
console.log(localVar); // Accessible (closure)
}
innerFunction();
}
demonstrateScope();
console.log(localVar); // Error: localVar is not defined
Arrow Functions (ES6)
Arrow functions provide a concise syntax for writing functions and have different behavior with the this
keyword compared to regular functions.
Basic Syntax
// Regular function
const addRegular = function(a, b) {
return a + b;
};
// Arrow function equivalent
const addArrow = (a, b) => {
return a + b;
};
// Implicit return (single expression)
const addImplicit = (a, b) => a + b;
console.log(addArrow(2, 3)); // 5
console.log(addImplicit(2, 3)); // 5
Single Parameter Simplification
// No parentheses needed for single parameter
const square = x => x * x;
// No parameters - empty parentheses
const greet = () => "Hello World!";
console.log(square(5)); // 25
console.log(greet()); // "Hello World!"
Returning Objects
// Returning object literal - requires parentheses
const createUser = (name, age) => ({ name, age });
// Equivalent regular function
const createUserRegular = function(name, age) {
return { name, age };
};
console.log(createUser("Alice", 30)); // {name: "Alice", age: 30}
this Binding in Arrow Functions
const person = {
name: "Alice",
tasks: ["task1", "task2", "task3"],
showTasksRegular: function() {
this.tasks.forEach(function(task) {
console.log(`${this.name}: ${task}`); // this is undefined
});
},
showTasksArrow: function() {
this.tasks.forEach(task => {
console.log(`${this.name}: ${task}`); // this refers to person
});
}
};
person.showTasksArrow(); // Works correctly
When Not to Use Arrow Functions
// Methods in objects (usually)
const obj = {
value: 10,
// Bad - arrow function doesn't have its own this
increment: () => {
this.value++; // this is not obj
},
// Good - regular function
incrementProper: function() {
this.value++;
}
};
// Constructor functions
const Person = function(name) {
this.name = name;
};
// Arrow functions can't be used as constructors
// const PersonArrow = (name) => { this.name = name; }; // Error
// Event handlers that need dynamic this
button.addEventListener('click', function() {
this.classList.toggle('active'); // this is the button
});
// With arrow function, this would be the surrounding context
ES6 Modules - Import and Export
ES6 modules allow you to organize code across multiple files, making it reusable and maintainable. Modules have their own scope and use explicit imports and exports.
Named Exports
// math.js - exporting multiple functions
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// Alternative: export at the end
const subtract = (a, b) => a - b;
const divide = (a, b) => a / b;
export { subtract, divide };
Default Export
// calculator.js - default export
export default class Calculator {
constructor() {
this.value = 0;
}
add(num) {
this.value += num;
return this;
}
getValue() {
return this.value;
}
}
// Alternative default export syntax
// export { Calculator as default };
Importing Modules
// main.js - importing from math.js
import { PI, add, multiply } from './math.js';
import { subtract as sub } from './math.js'; // Renaming import
import Calculator from './calculator.js'; // Default import
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
const calc = new Calculator();
calc.add(5);
Namespace Import
// Import everything as a namespace
import * as MathUtils from './math.js';
console.log(MathUtils.add(2, 3)); // 5
console.log(MathUtils.PI); // 3.14159
Dynamic Imports
// Lazy loading modules
async function loadModule() {
try {
const module = await import('./math.js');
console.log(module.add(2, 3)); // 5
} catch (error) {
console.error('Module loading failed:', error);
}
}
// Conditionally load modules
if (userNeedsFeature) {
import('./advanced-feature.js')
.then(module => {
module.initialize();
});
}
HTML Setup
<!-- Include type="module" in script tag -->
<script type="module" src="main.js"></script>
<!-- Or inline module -->
<script type="module">
import { greet } from './utils.js';
greet('World');
</script>
DOM Manipulation in JavaScript
The Document Object Model (DOM) represents the structure of an HTML document. JavaScript can manipulate the DOM to dynamically change content, styles, and structure.
Selecting Elements
// Different selection methods
const byId = document.getElementById('header');
const byClass = document.getElementsByClassName('item'); // HTMLCollection
const byTag = document.getElementsByTagName('div'); // HTMLCollection
// Modern methods (return NodeList)
const querySingle = document.querySelector('.main'); // First match
const queryAll = document.querySelectorAll('.item'); // All matches
console.log(byId, byClass, queryAll);
Creating and Modifying Elements
// Creating new elements
const newDiv = document.createElement('div');
const newParagraph = document.createElement('p');
// Setting content and attributes
newDiv.textContent = 'Hello World!';
newDiv.id = 'myDiv';
newDiv.className = 'container highlighted';
// Or using classList for better control
newDiv.classList.add('active');
newDiv.classList.remove('highlighted');
newDiv.classList.toggle('visible');
// Setting styles
newDiv.style.backgroundColor = 'blue';
newDiv.style.fontSize = '18px';
// Adding to DOM
document.body.appendChild(newDiv);
Modifying Existing Elements
// Changing content
const element = document.querySelector('#content');
element.innerHTML = 'New content'; // Parse HTML
element.textContent = 'Safe text content'; // Text only
element.innerText = 'Styled text'; // Respects CSS
// Modifying attributes
element.setAttribute('data-custom', 'value');
const value = element.getAttribute('data-custom');
element.removeAttribute('data-custom');
// Data attributes
element.dataset.userId = '123'; // data-user-id
console.log(element.dataset.userId); // '123'
Event Handling
const button = document.querySelector('#myButton');
// Adding event listeners
button.addEventListener('click', function(event) {
console.log('Button clicked!', event);
this.style.backgroundColor = 'red'; // this is the button
});
// Arrow function (different this binding)
button.addEventListener('click', (event) => {
console.log('Button clicked!', event.target);
});
// Multiple events
const input = document.querySelector('#myInput');
input.addEventListener('focus', handleFocus);
input.addEventListener('blur', handleBlur);
input.addEventListener('input', handleInput);
function handleFocus() {
this.style.borderColor = 'blue';
}
// Removing event listeners
input.removeEventListener('focus', handleFocus);
Traversing the DOM
const element = document.querySelector('.item');
// Navigating relationships
console.log(element.parentNode);
console.log(element.children); // Element children only
console.log(element.childNodes); // All nodes (text, comments, etc.)
console.log(element.firstElementChild);
console.log(element.lastElementChild);
console.log(element.nextElementSibling);
console.log(element.previousElementSibling);
// Finding within element
const nested = element.querySelector('.nested');
Practical Example: Dynamic List
// Create interactive list
const list = document.querySelector('#todoList');
const input = document.querySelector('#todoInput');
const button = document.querySelector('#addButton');
button.addEventListener('click', addTodo);
function addTodo() {
const text = input.value.trim();
if (text === '') return;
const li = document.createElement('li');
li.textContent = text;
li.classList.add('todo-item');
// Add delete button
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Delete';
deleteBtn.addEventListener('click', function() {
li.remove();
});
li.appendChild(deleteBtn);
list.appendChild(li);
input.value = '';
}
ES6 (ECMAScript 2015) Introduction
ES6 (ECMAScript 2015) was a major update to JavaScript that introduced many new features and syntax improvements that make JavaScript more powerful and easier to work with.
What is ES6?
ES6, also known as ECMAScript 2015, is the 6th edition of the ECMAScript language specification. It brought significant enhancements to JavaScript including:
- New variable declarations (
let
andconst
) - Arrow functions
- Template literals
- Destructuring assignments
- Classes and modules
- Promises for async operations
- And many more features...
Browser Support
Modern browsers have excellent ES6 support, but for older browsers you might need transpilation:
// Check if a feature is supported
console.log('Arrow functions supported:', () => {} instanceof Function);
console.log('Template literals supported:', `test` === 'test');
console.log('let/const supported:', (() => { try { eval('let x = 1;'); return true; } catch(e) { return false; } })());
// Transpilation with Babel example (concept)
// ES6 code → Babel → ES5 compatible code
Key ES6 Features Overview
// Before ES6
var name = "John";
var message = "Hello " + name + "!";
var obj = { name: name, age: 30 };
function multiply(a, b) { return a * b; }
// With ES6
let name = "John";
const age = 30;
const message = `Hello ${name}!`;
const obj = { name, age };
const multiply = (a, b) => a * b;
console.log(message); // "Hello John!"
Strict Mode in ES6
ES6 modules and classes automatically use strict mode:
// In ES6 modules, this is automatically strict mode
// Common strict mode benefits:
// - Prevents accidental global variables
// - Makes assignments that would silently fail throw errors
// - Disables confusing features
// Example of strict mode preventing errors
function strictExample() {
'use strict';
// undeclaredVar = 10; // Throws ReferenceError
return "Strict mode enabled";
}
console.log(strictExample());
let and const Declarations
ES6 introduced let
and const
as new ways to declare variables, addressing issues with var
and providing block scoping.
Block Scope
let
and const
are block-scoped, unlike var
which is function-scoped:
// var vs let/const scope
function scopeExample() {
if (true) {
var varVariable = "I'm var";
let letVariable = "I'm let";
const constVariable = "I'm const";
}
console.log(varVariable); // "I'm var" - accessible
// console.log(letVariable); // ReferenceError - not accessible
// console.log(constVariable); // ReferenceError - not accessible
}
scopeExample();
let Declaration
let
allows variable reassignment but provides block scoping:
// let allows reassignment
let counter = 0;
counter = 1; // OK
counter++; // OK
// Block scoping with let
for (let i = 0; i < 3; i++) {
console.log(i); // 0, 1, 2 - each iteration has its own i
}
// console.log(i); // ReferenceError: i is not defined
// No hoisting to the scope (temporal dead zone)
// console.log(notYetDeclared); // ReferenceError
let notYetDeclared = "now declared";
const Declaration
const
creates read-only references, but doesn't make objects/arrays immutable:
// const prevents reassignment
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable
// But objects and arrays are still mutable
const person = { name: "Alice", age: 30 };
person.age = 31; // OK - modifying property
person.city = "New York"; // OK - adding property
// person = { name: "Bob" }; // TypeError - cannot reassign
const numbers = [1, 2, 3];
numbers.push(4); // OK - modifying array
numbers[0] = 10; // OK - changing element
// numbers = [5, 6, 7]; // TypeError - cannot reassign
Best Practices
// Use const by default
const API_URL = "https://api.example.com";
const DEFAULT_CONFIG = { timeout: 5000, retries: 3 };
// Use let when you need to reassign
let isLoading = false;
let userPreferences = {};
function updatePreferences(newPrefs) {
userPreferences = { ...userPreferences, ...newPrefs }; // Reassignment needed
}
// Avoid var in modern code
// var legacyVar = "don't use this";
Temporal Dead Zone (TDZ)
// Variables declared with let/const exist in TDZ until declaration
function tdzExample() {
// Start of block - variable in TDZ
// console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization
let myLet = "declared";
console.log(myLet); // "declared" - accessible after declaration
}
tdzExample();
Template Literals
Template literals (template strings) provide an easy way to create strings with embedded expressions and multi-line capabilities using backticks (`).
Basic String Interpolation
const name = "Alice";
const age = 30;
// Before ES6 - string concatenation
const oldWay = "Hello, my name is " + name + " and I am " + age + " years old.";
// With template literals
const newWay = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(newWay); // "Hello, my name is Alice and I am 30 years old."
Multi-line Strings
// Before ES6 - messy multi-line strings
const oldMultiLine = "This is line one\n" +
"This is line two\n" +
"This is line three";
// With template literals
const newMultiLine = `This is line one
This is line two
This is line three`;
console.log(newMultiLine);
// Output:
// This is line one
// This is line two
// This is line three
Expression Evaluation
// Any valid JavaScript expression can be embedded
const a = 5;
const b = 10;
console.log(`The sum is ${a + b}`); // "The sum is 15"
console.log(`Is a greater than b? ${a > b}`); // "Is a greater than b? false"
// Function calls
function getGreeting() { return "Hello"; }
console.log(`${getGreeting()} World!`); // "Hello World!"
// Ternary operators
const score = 85;
console.log(`You ${score >= 60 ? 'passed' : 'failed'} the test!`); // "You passed the test!"
Tagged Templates
// Tagged templates allow custom string processing
function highlight(strings, ...values) {
let result = '';
strings.forEach((string, i) => {
result += string;
if (i < values.length) {
result += `<mark>${values[i]}</mark>`;
}
});
return result;
}
const name = "Alice";
const age = 30;
const highlighted = highlight`Hello, my name is ${name} and I am ${age} years old.`;
console.log(highlighted);
// "Hello, my name is <mark>Alice</mark> and I am <mark>30</mark> years old."
// Practical example: SQL query formatting
function sql(strings, ...values) {
return strings.reduce((query, string, i) => {
return query + string + (values[i] ? `'${values[i]}'` : '');
}, '');
}
const table = "users";
const id = 123;
const query = sql`SELECT * FROM ${table} WHERE id = ${id}`;
console.log(query); // "SELECT * FROM 'users' WHERE id = '123'"
Advanced Usage
// Nested template literals
const isMember = true;
const discount = 0.1;
const price = 100;
const message = `Total: $${price * (isMember ? (1 - discount) : 1)} ${isMember ? '(member discount applied)' : ''}`;
console.log(message); // "Total: $90 (member discount applied)"
// Template literals with object destructuring
const user = { firstName: "John", lastName: "Doe" };
const greeting = `Hello, ${user.firstName} ${user.lastName}!`;
console.log(greeting); // "Hello, John Doe!"
// Internationalization
const amount = 1234.56;
const currency = "USD";
const formatted = `Total: ${new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency
}).format(amount)}`;
console.log(formatted); // "Total: $1,234.56"
Destructuring Assignment
Destructuring allows you to extract values from arrays or properties from objects into distinct variables using a syntax that mirrors array and object literals.
Array Destructuring
// Basic array destructuring
const numbers = [1, 2, 3, 4, 5];
const [first, second, third] = numbers;
console.log(first, second, third); // 1, 2, 3
// Skipping elements
const [a, , c] = numbers;
console.log(a, c); // 1, 3
// Rest pattern
const [x, y, ...rest] = numbers;
console.log(x, y, rest); // 1, 2, [3, 4, 5]
// Default values
const [p = 10, q = 20] = [1];
console.log(p, q); // 1, 20
Object Destructuring
// Basic object destructuring
const person = { name: "Alice", age: 30, city: "New York" };
const { name, age, city } = person;
console.log(name, age, city); // "Alice", 30, "New York"
// Renaming variables
const { name: personName, age: personAge } = person;
console.log(personName, personAge); // "Alice", 30
// Default values
const { country = "USA" } = person;
console.log(country); // "USA" (default used)
// Nested destructuring
const user = {
id: 1,
profile: {
firstName: "John",
lastName: "Doe",
address: { city: "Boston" }
}
};
const { profile: { firstName, lastName, address: { city: userCity } } } = user;
console.log(firstName, lastName, userCity); // "John", "Doe", "Boston"
Function Parameter Destructuring
// Destructuring function parameters
function printUser({ name, age, city = "Unknown" }) {
console.log(`${name} is ${age} years old and lives in ${city}`);
}
const user = { name: "Bob", age: 25 };
printUser(user); // "Bob is 25 years old and lives in Unknown"
// Array parameters
function getFirstAndLast([first, , last]) {
return { first, last };
}
const result = getFirstAndLast([1, 2, 3, 4]);
console.log(result); // { first: 1, last: 3 }
Practical Use Cases
// Swapping variables
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1
// Function returning multiple values
function getStats(numbers) {
return {
min: Math.min(...numbers),
max: Math.max(...numbers),
avg: numbers.reduce((a, b) => a + b) / numbers.length
};
}
const { min, max, avg } = getStats([1, 2, 3, 4, 5]);
console.log(`Min: ${min}, Max: ${max}, Average: ${avg}`);
// Working with React props (conceptual)
// function UserCard({ name, email, avatar, onClick }) {
// return {name} - {email};
// }
// Import statements (conceptual)
// import { useState, useEffect } from 'react';
Advanced Patterns
// Dynamic property names
const key = "status";
const { [key]: statusValue } = { status: "active", other: "value" };
console.log(statusValue); // "active"
// Combined array and object destructuring
const data = [
{ id: 1, value: "first" },
{ id: 2, value: "second" }
];
const [, { value: secondValue }] = data;
console.log(secondValue); // "second"
// Destructuring with computed properties
function getConfig(key) {
const configs = {
api: { url: "https://api.example.com", timeout: 5000 },
ui: { theme: "dark", language: "en" }
};
return configs[key];
}
const { url: apiUrl, timeout } = getConfig("api");
console.log(apiUrl, timeout); // "https://api.example.com", 5000
Asynchronous JavaScript Introduction
Asynchronous programming allows JavaScript to perform long-running tasks without blocking the main thread, enabling responsive web applications.
Why Asynchronous?
JavaScript is single-threaded, so blocking operations would freeze the entire application:
// Synchronous (blocking) example
console.log("Start");
// This would block everything if it took a long time
// const data = syncNetworkRequest(); // Everything waits here
console.log("End");
// Asynchronous (non-blocking) example
console.log("Start");
setTimeout(() => {
console.log("Async operation completed");
}, 1000);
console.log("End");
// Output: Start, End, Async operation completed
The Event Loop
JavaScript uses an event loop to handle asynchronous operations:
console.log("Script start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("Script end");
// Output order:
// Script start
// Script end
// Promise (microtask)
// setTimeout (macrotask)
Common Async Operations
// Timers
setTimeout(() => console.log("Timeout"), 1000);
setInterval(() => console.log("Interval"), 2000);
// Network requests (conceptual)
// fetch('https://api.example.com/data')
// .then(response => response.json())
// .then(data => console.log(data));
// File operations (Node.js)
// fs.readFile('file.txt', 'utf8', (err, data) => {
// if (err) throw err;
// console.log(data);
// });
// User interactions
document.addEventListener('click', (event) => {
console.log('Clicked at:', event.clientX, event.clientY);
});
Callback Pattern (Traditional)
// Traditional callback approach
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: "Example" };
callback(null, data); // Node.js convention: error first
}, 1000);
}
// Using the callback
fetchData((error, data) => {
if (error) {
console.error("Error:", error);
return;
}
console.log("Data received:", data);
});
// Callback hell (pyramid of doom)
function complexOperation(callback) {
step1((err1, result1) => {
if (err1) return callback(err1);
step2(result1, (err2, result2) => {
if (err2) return callback(err2);
step3(result2, (err3, result3) => {
if (err3) return callback(err3);
callback(null, result3);
});
});
});
}
Async Evolution
// Evolution of async patterns in JavaScript
// 1. Callbacks (old)
// getData(function(error, data) { ... });
// 2. Promises (ES6)
// getData().then(data => ...).catch(error => ...);
// 3. Async/Await (ES2017)
// const data = await getData();
Promises in JavaScript
A Promise represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises provide a cleaner alternative to callbacks.
Creating Promises
// Creating a promise
const myPromise = new Promise((resolve, reject) => {
// Asynchronous operation
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve("Operation completed successfully!");
} else {
reject(new Error("Operation failed!"));
}
}, 1000);
});
// Using the promise
myPromise
.then(result => {
console.log("Success:", result);
})
.catch(error => {
console.error("Error:", error.message);
})
.finally(() => {
console.log("Operation attempted (cleanup)");
});
Promise States
// Promise has three states:
// - Pending: initial state
// - Fulfilled: operation completed successfully
// - Rejected: operation failed
const pendingPromise = new Promise(() => {}); // Always pending
console.log(pendingPromise); // Promise { <pending> }
const fulfilledPromise = Promise.resolve("Success!");
console.log(fulfilledPromise); // Promise { "Success!" }
const rejectedPromise = Promise.reject(new Error("Failure"));
console.log(rejectedPromise); // Promise { <rejected> Error: Failure }
Promise Chaining
// Promises can be chained
function asyncOperation(value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value * 2), 1000);
});
}
asyncOperation(5)
.then(result => {
console.log("First result:", result); // 10
return asyncOperation(result);
})
.then(result => {
console.log("Second result:", result); // 20
return result + 5;
})
.then(result => {
console.log("Final result:", result); // 25
})
.catch(error => {
console.error("Chain failed:", error);
});
Promise Static Methods
// Promise.all - wait for all promises
const promise1 = Promise.resolve(3);
const promise2 = 42; // Non-promise values are OK
const promise3 = new Promise((resolve) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3])
.then(values => {
console.log(values); // [3, 42, "foo"]
});
// Promise.race - first settled promise wins
const fast = new Promise(resolve => setTimeout(resolve, 100, 'fast'));
const slow = new Promise(resolve => setTimeout(resolve, 500, 'slow'));
Promise.race([fast, slow])
.then(result => {
console.log(result); // "fast" (wins the race)
});
// Promise.allSettled - wait for all to complete (success or failure)
const promises = [
Promise.resolve('success'),
Promise.reject('error'),
Promise.resolve('another success')
];
Promise.allSettled(promises)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index}:`, result.value);
} else {
console.log(`Promise ${index} failed:`, result.reason);
}
});
});
Practical Promise Examples
// Simulating API calls
function fetchUser(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: userId, name: `User ${userId}` });
}, 1000);
});
}
function fetchUserPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([`Post 1 by User ${userId}`, `Post 2 by User ${userId}`]);
}, 800);
});
}
// Fetch user and their posts
fetchUser(1)
.then(user => {
console.log("User:", user);
return fetchUserPosts(user.id);
})
.then(posts => {
console.log("Posts:", posts);
})
.catch(error => {
console.error("Error loading data:", error);
});
// Parallel execution
Promise.all([fetchUser(1), fetchUserPosts(1)])
.then(([user, posts]) => {
console.log("User and posts:", user, posts);
});
Async/Await
Async/await is syntactic sugar built on top of promises that makes asynchronous code look and behave more like synchronous code, improving readability and maintainability.
Basic Async/Await
// Async function always returns a promise
async function fetchData() {
return "Data fetched!";
}
// Equivalent to:
function fetchDataPromise() {
return Promise.resolve("Data fetched!");
}
// Using await
async function getData() {
const data = await fetchData();
console.log(data); // "Data fetched!"
}
getData();
Error Handling
// Using try/catch with async/await
async function fetchWithRetry() {
try {
const data = await fetchData();
console.log("Success:", data);
} catch (error) {
console.error("Error:", error.message);
}
}
// Simulating an async function that might fail
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.3;
success ? resolve("Data received!") : reject(new Error("Network error"));
}, 1000);
});
}
// Multiple await calls with error handling
async function processUserData(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchUserPosts(userId);
const settings = await fetchUserSettings(userId);
return { user, posts, settings };
} catch (error) {
console.error("Failed to process user data:", error);
throw error; // Re-throw to let caller handle
}
}
Parallel Execution with Async/Await
// Sequential execution (slow)
async function sequentialFetch() {
console.time('sequential');
const user = await fetchUser(1); // 1 second
const posts = await fetchUserPosts(1); // 0.8 seconds
const settings = await fetchSettings(1); // 0.5 seconds
console.timeEnd('sequential'); // ~2.3 seconds
return { user, posts, settings };
}
// Parallel execution (fast)
async function parallelFetch() {
console.time('parallel');
const [user, posts, settings] = await Promise.all([
fetchUser(1),
fetchUserPosts(1),
fetchSettings(1)
]);
console.timeEnd('parallel'); // ~1 second (longest operation)
return { user, posts, settings };
}
// Simulated async functions
async function fetchUser(id) {
await new Promise(resolve => setTimeout(resolve, 1000));
return { id, name: `User ${id}` };
}
async function fetchUserPosts(id) {
await new Promise(resolve => setTimeout(resolve, 800));
return [`Post 1`, `Post 2`];
}
async function fetchSettings(id) {
await new Promise(resolve => setTimeout(resolve, 500));
return { theme: 'dark', notifications: true };
}
Async Iteration
// for-await-of loop for async iterables
async function processItems() {
const asyncItems = [
Promise.resolve('item1'),
Promise.resolve('item2'),
Promise.resolve('item3')
];
for await (const item of asyncItems) {
console.log(item);
}
}
// With real async operations
async function processUserIds(userIds) {
for await (const userId of userIds) {
try {
const user = await fetchUser(userId);
console.log('Processed:', user.name);
} catch (error) {
console.error('Failed to process user:', userId, error);
}
}
}
processUserIds([1, 2, 3, 4, 5]);
Practical Async/Await Patterns
// Retry pattern with async/await
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
console.log(`Attempt ${i + 1} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// Timeout pattern
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
// Multiple async operations with error isolation
async function loadDashboardData(userId) {
const [user, posts, recommendations] = await Promise.allSettled([
fetchUser(userId),
fetchUserPosts(userId),
fetchRecommendations(userId)
]);
return {
user: user.status === 'fulfilled' ? user.value : null,
posts: posts.status === 'fulfilled' ? posts.value : [],
recommendations: recommendations.status === 'fulfilled' ? recommendations.value : []
};
}
Advanced JavaScript Concepts
Beyond the basics, JavaScript offers many advanced features for writing efficient and maintainable code.
1. Closures
Closures allow functions to access variables from an outer function's scope even after the outer function has returned:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
2. Promises and Async/Await
Modern JavaScript provides elegant ways to handle asynchronous operations:
// Using Promises
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// Using async/await (cleaner syntax)
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
3. Modules
ES6 modules allow you to organize code across multiple files:
// math.js (exporting)
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
// main.js (importing)
import { add, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
Exploring these advanced topics can significantly enhance your JavaScript programming capabilities.