Comprehensive JavaScript Guide

A complete reference for JavaScript from basics to advanced concepts

Introduction to JavaScript

JavaScript is a high-level, interpreted programming language that conforms to the ECMAScript specification. It is a language that is also characterized as dynamic, weakly typed, prototype-based, and multi-paradigm.

JavaScript Timeline

  • 1995 - JavaScript was created by Brendan Eich at Netscape
  • 1997 - ECMAScript 1 (ES1) was released
  • 1998 - ECMAScript 2 (ES2) was released
  • 1999 - ECMAScript 3 (ES3) was released
  • 2009 - ECMAScript 5 (ES5) was released
  • 2015 - ECMAScript 2015 (ES6) was released with major new features
  • 2016-Present - Annual releases (ES2016, ES2017, etc.) with incremental updates

JavaScript was originally designed to add interactivity to web pages, but it has evolved into a versatile language used for both client-side and server-side development. Today, JavaScript powers everything from simple web animations to complex single-page applications, mobile apps, desktop applications, and even server-side applications.

How JavaScript Works

JavaScript is primarily a client-side language, which means it runs in the user's browser rather than on the web server. When a web page is loaded, the browser creates a Document Object Model (DOM) of the page, which JavaScript can then manipulate.

JavaScript is an interpreted language, which means the code is executed line by line, rather than being compiled before execution. Modern browsers use Just-In-Time (JIT) compilation to improve performance.

JavaScript, HTML, and CSS

JavaScript works alongside HTML and CSS to create interactive web pages:

JavaScript Environments

JavaScript can run in various environments:

JavaScript Basics

Before diving into the details, let's understand the basic concepts of JavaScript.

Adding JavaScript to HTML

There are three ways to add JavaScript to an HTML document:

1. Internal JavaScript

<script>
    // JavaScript code goes here
    document.getElementById("demo").innerHTML = "Hello, World!";
</script>

2. External JavaScript

<script src="script.js"></script>

3. Inline JavaScript

<button onclick="alert('Hello, World!')">Click Me</button>

JavaScript Syntax

JavaScript syntax is the set of rules that define how JavaScript programs are constructed.

Statements

JavaScript statements are commands to the browser. They are composed of values, operators, expressions, keywords, and comments.

// This is a statement
let x = 5;

// This is another statement
console.log("Hello, World!");

// Multiple statements can be grouped in a block using curly braces
{
    let x = 5;
    console.log(x);
}

Comments

Comments are used to explain JavaScript code and make it more readable. They are ignored by the browser.

// This is a single-line comment

/* This is a
   multi-line comment */

Variables

Variables are containers for storing data values. In JavaScript, there are three ways to declare a variable:

// Using var (function-scoped, older way)
var x = 5;

// Using let (block-scoped, introduced in ES6)
let y = 10;

// Using const (block-scoped, constant value, introduced in ES6)
const z = 15;

Operators

JavaScript operators are symbols that are used to perform operations on operands.

Type Operators Example
Arithmetic +, -, *, /, %, **, ++, -- let sum = 5 + 10;
Assignment =, +=, -=, *=, /=, %=, **= let x = 10; x += 5;
Comparison ==, ===, !=, !==, >, <, >=, <= if (x === 10) { }
Logical &&, ||, ! if (x > 5 && y < 10) { }
String +, += let greeting = "Hello" + " " + "World";
Conditional (Ternary) ?: let status = (age >= 18) ? "Adult" : "Minor";

Control Structures

Control structures are used to control the flow of execution in a program.

// if statement
if (condition) {
    // code to be executed if condition is true
} else if (anotherCondition) {
    // code to be executed if anotherCondition is true
} else {
    // code to be executed if no condition is true
}

// switch statement
switch (expression) {
    case value1:
        // code to be executed if expression === value1
        break;
    case value2:
        // code to be executed if expression === value2
        break;
    default:
        // code to be executed if expression doesn't match any case
}

// for loop
for (let i = 0; i < 5; i++) {
    // code to be executed 5 times
}

// while loop
let i = 0;
while (i < 5) {
    // code to be executed as long as i < 5
    i++;
}

// do...while loop
let i = 0;
do {
    // code to be executed at least once, then as long as i < 5
    i++;
} while (i < 5);

// for...in loop (for objects)
for (let key in object) {
    // code to be executed for each key in object
}

// for...of loop (for iterables like arrays)
for (let value of array) {
    // code to be executed for each value in array
}

For more detailed information on JavaScript basics, visit our JavaScript Basics page.

JavaScript Data Types

JavaScript has several built-in data types that can be categorized as primitive and non-primitive (reference) types.

Primitive Data Types

Primitive data types are immutable (cannot be changed) and are passed by value.

Data Type Description Example
String Represents textual data "Hello, World!", 'JavaScript'
Number Represents numeric values 42, 3.14, NaN, Infinity
Boolean Represents logical values true, false
Undefined Represents a variable that has been declared but not assigned a value undefined
Null Represents the intentional absence of any object value null
Symbol Represents a unique identifier Symbol('description')
BigInt Represents integers larger than the Number type can handle 9007199254740991n

Non-Primitive (Reference) Data Types

Non-primitive data types are mutable (can be changed) and are passed by reference.

Data Type Description Example
Object Represents a collection of related data { name: "John", age: 30 }
Array Represents a list-like collection of values [1, 2, 3, 4]
Function Represents a reusable block of code function() { return "Hello"; }
Date Represents a date and time new Date()
RegExp Represents a regular expression /pattern/
Map Represents a collection of key-value pairs new Map()
Set Represents a collection of unique values new Set()

Type Conversion

JavaScript is a loosely typed language, which means variables can change types. There are two types of type conversion:

Implicit Conversion (Type Coercion)

let result = "5" + 2;     // "52" (number is converted to string)
console.log(result);

result = "5" - 2;         // 3 (string is converted to number)
console.log(result);

result = "5" * "2";       // 10 (both strings are converted to numbers)
console.log(result);

result = 5 + true;        // 6 (true is converted to 1)
console.log(result);

result = 5 + false;       // 5 (false is converted to 0)
console.log(result);

Explicit Conversion

// String to Number
let num = Number("5");      // 5
console.log(num);

// Number to String
let str = String(5);        // "5"
console.log(str);

// Value to Boolean
let bool = Boolean(0);      // false
console.log(bool);

bool = Boolean(1);          // true
console.log(bool);

bool = Boolean("");         // false
console.log(bool);

bool = Boolean("Hello");    // true
console.log(bool);

Arrays

Arrays are used to store multiple values in a single variable. They are zero-indexed, meaning the first element is at index 0.

// Creating an array
let fruits = ["Apple", "Banana", "Orange"];

// Accessing array elements
let firstFruit = fruits[0];  // "Apple"

// Modifying array elements
fruits[1] = "Mango";  // Now fruits is ["Apple", "Mango", "Orange"]

// Array length
let length = fruits.length;  // 3

// Array methods
fruits.push("Pineapple");  // Add to the end
fruits.pop();  // Remove from the end
fruits.unshift("Strawberry");  // Add to the beginning
fruits.shift();  // Remove from the beginning
fruits.splice(1, 1, "Kiwi");  // Remove 1 element at index 1 and add "Kiwi"
let newFruits = fruits.slice(1, 3);  // Create a new array from index 1 to 2
let combined = fruits.concat(["Grape", "Watermelon"]);  // Combine arrays
let joined = fruits.join(", ");  // Join elements into a string
let index = fruits.indexOf("Orange");  // Find the index of an element
let includes = fruits.includes("Apple");  // Check if an element exists

Strings

Strings are used to store and manipulate text. They are immutable, meaning they cannot be changed once created.

// Creating a string
let text = "Hello, World!";

// String length
let length = text.length;  // 13

// Accessing characters
let firstChar = text[0];  // "H"
let lastChar = text[text.length - 1];  // "!"

// String methods
let upperCase = text.toUpperCase();  // "HELLO, WORLD!"
let lowerCase = text.toLowerCase();  // "hello, world!"
let substring = text.substring(0, 5);  // "Hello"
let replaced = text.replace("World", "JavaScript");  // "Hello, JavaScript!"
let split = text.split(", ");  // ["Hello", "World!"]
let trimmed = "   Hello   ".trim();  // "Hello"
let includes = text.includes("World");  // true
let startsWith = text.startsWith("Hello");  // true
let endsWith = text.endsWith("!");  // true
let indexOf = text.indexOf("World");  // 7
let charAt = text.charAt(0);  // "H"

JavaScript Functions

Functions are one of the fundamental building blocks in JavaScript. A function is a reusable block of code designed to perform a particular task.

Function Declaration

A function declaration defines a named function.

function greet(name) {
    return "Hello, " + name + "!";
}

// Calling the function
let message = greet("John");
console.log(message);  // "Hello, John!"

Function Expression

A function expression defines a function as part of a larger expression, typically a variable assignment.

let greet = function(name) {
    return "Hello, " + name + "!";
};

// Calling the function
let message = greet("John");
console.log(message);  // "Hello, John!"

Arrow Functions

Arrow functions provide a shorter syntax for writing function expressions. They were introduced in ES6.

let greet = (name) => {
    return "Hello, " + name + "!";
};

// Shorter syntax for simple functions
let greetShort = name => "Hello, " + name + "!";

// Calling the function
let message = greet("John");
console.log(message);  // "Hello, John!"

Function Parameters

Function parameters are the names listed in the function definition. Function arguments are the real values passed to the function.

// Function with multiple parameters
function add(a, b) {
    return a + b;
}

let sum = add(5, 3);
console.log(sum);  // 8

// Default parameters (ES6)
function greet(name = "Guest") {
    return "Hello, " + name + "!";
}

console.log(greet());  // "Hello, Guest!"
console.log(greet("John"));  // "Hello, John!"

// Rest parameters (ES6)
function sum(...numbers) {
    let total = 0;
    for (let number of numbers) {
        total += number;
    }
    return total;
}

console.log(sum(1, 2, 3, 4, 5));  // 15

Scope

Scope determines the accessibility of variables, objects, and functions from different parts of the code.

// Global scope
let globalVar = "I am global";

function myFunction() {
    // Function scope
    let functionVar = "I am function-scoped";
    console.log(globalVar);  // "I am global"
    
    if (true) {
        // Block scope (with let and const)
        let blockVar = "I am block-scoped";
        const blockConst = "I am also block-scoped";
        var functionScopedVar = "I am function-scoped, not block-scoped";
        console.log(blockVar);  // "I am block-scoped"
    }
    
    // console.log(blockVar);  // Error: blockVar is not defined
    // console.log(blockConst);  // Error: blockConst is not defined
    console.log(functionScopedVar);  // "I am function-scoped, not block-scoped"
}

myFunction();
// console.log(functionVar);  // Error: functionVar is not defined

Closures

A closure is the combination of a function bundled together with references to its surrounding state (the lexical environment).

function createCounter() {
    let count = 0;
    
    return function() {
        count++;
        return count;
    };
}

let counter = createCounter();
console.log(counter());  // 1
console.log(counter());  // 2
console.log(counter());  // 3

Higher-Order Functions

Higher-order functions are functions that take other functions as arguments or return functions as their results.

// Function that takes a function as an argument
function doOperation(x, y, operation) {
    return operation(x, y);
}

let sum = doOperation(5, 3, function(a, b) {
    return a + b;
});
console.log(sum);  // 8

// Function that returns a function
function createMultiplier(factor) {
    return function(number) {
        return number * factor;
    };
}

let double = createMultiplier(2);
let triple = createMultiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

JavaScript Objects

Objects are collections of key-value pairs. They are used to store related data and functionality.

Creating Objects

There are several ways to create objects in JavaScript:

// Object literal
let person = {
    firstName: "John",
    lastName: "Doe",
    age: 30,
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
};

// Using the new Object() syntax
let car = new Object();
car.make = "Toyota";
car.model = "Corolla";
car.year = 2020;

// Using a constructor function
function Book(title, author, year) {
    this.title = title;
    this.author = author;
    this.year = year;
    this.getSummary = function() {
        return `${this.title} was written by ${this.author} in ${this.year}`;
    };
}

let book = new Book("The Hobbit", "J.R.R. Tolkien", 1937);

// Using Object.create()
let animal = {
    type: "Animal",
    displayType: function() {
        console.log(this.type);
    }
};

let dog = Object.create(animal);
dog.type = "Dog";

Accessing Object Properties

There are two ways to access object properties:

// Dot notation
let firstName = person.firstName;

// Bracket notation
let lastName = person["lastName"];

Object Methods

Object methods are functions that are stored as object properties.

let person = {
    firstName: "John",
    lastName: "Doe",
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
};

// Calling the method
console.log(person.fullName());  // "John Doe"

// Shorthand method syntax (ES6)
let calculator = {
    add(a, b) {
        return a + b;
    },
    subtract(a, b) {
        return a - b;
    }
};

console.log(calculator.add(5, 3));  // 8

Prototypes and Inheritance

JavaScript uses prototypal inheritance, where objects inherit properties and methods from other objects.

// Constructor function
function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

// Adding a method to the prototype
Person.prototype.fullName = function() {
    return this.firstName + " " + this.lastName;
};

let person1 = new Person("John", "Doe");
let person2 = new Person("Jane", "Smith");

console.log(person1.fullName());  // "John Doe"
console.log(person2.fullName());  // "Jane Smith"

// Inheritance
function Student(firstName, lastName, school) {
    Person.call(this, firstName, lastName);
    this.school = school;
}

// Inherit the Person prototype
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

// Add a method to Student prototype
Student.prototype.getSchool = function() {
    return this.school;
};

let student = new Student("Alice", "Johnson", "MIT");
console.log(student.fullName());  // "Alice Johnson"
console.log(student.getSchool());  // "MIT"

Classes (ES6)

ES6 introduced a class syntax that provides a clearer and more concise way to create objects and deal with inheritance.

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    fullName() {
        return this.firstName + " " + this.lastName;
    }
}

let person = new Person("John", "Doe");
console.log(person.fullName());  // "John Doe"

// Inheritance with classes
class Student extends Person {
    constructor(firstName, lastName, school) {
        super(firstName, lastName);
        this.school = school;
    }
    
    getSchool() {
        return this.school;
    }
}

let student = new Student("Alice", "Johnson", "MIT");
console.log(student.fullName());  // "Alice Johnson"
console.log(student.getSchool());  // "MIT"

Document Object Model (DOM)

The Document Object Model (DOM) is a programming interface for HTML and XML documents. It represents the page so that programs can change the document structure, style, and content.

Accessing DOM Elements

There are several ways to access DOM elements:

// By ID
let element = document.getElementById("myId");

// By class name
let elements = document.getElementsByClassName("myClass");

// By tag name
let paragraphs = document.getElementsByTagName("p");

// By CSS selector (returns the first matching element)
let element = document.querySelector(".myClass");

// By CSS selector (returns all matching elements)
let elements = document.querySelectorAll("p.intro");

Modifying DOM Elements

Once you have accessed a DOM element, you can modify its content, attributes, and style:

// Changing content
element.textContent = "New text content";
element.innerHTML = "New HTML content";

// Changing attributes
element.setAttribute("class", "newClass");
element.id = "newId";

// Changing style
element.style.color = "red";
element.style.fontSize = "20px";
element.style.display = "none";

Creating and Removing Elements

You can create new elements and add them to the DOM, or remove existing elements:

// Creating a new element
let newElement = document.createElement("div");
newElement.textContent = "New element";

// Adding the element to the DOM
document.body.appendChild(newElement);

// Inserting before another element
let referenceElement = document.getElementById("reference");
document.body.insertBefore(newElement, referenceElement);

// Removing an element
let elementToRemove = document.getElementById("remove");
elementToRemove.parentNode.removeChild(elementToRemove);

// Modern way to remove an element
elementToRemove.remove();

Event Handling

Events are actions that occur in the browser, such as a user clicking a button or a page finishing loading. You can use JavaScript to respond to these events:

// Using addEventListener
let button = document.getElementById("myButton");
button.addEventListener("click", function(event) {
    console.log("Button clicked!");
    console.log(event);
});

// Removing an event listener
function handleClick(event) {
    console.log("Button clicked!");
}

button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick);

// Event delegation
document.getElementById("parent").addEventListener("click", function(event) {
    if (event.target.matches(".child")) {
        console.log("Child element clicked!");
    }
});

Common DOM Events

Event Description
click Occurs when the element is clicked
dblclick Occurs when the element is double-clicked
mouseenter Occurs when the mouse pointer enters the element
mouseleave Occurs when the mouse pointer leaves the element
keydown Occurs when a key is pressed down
keyup Occurs when a key is released
submit Occurs when a form is submitted
load Occurs when an object has loaded
resize Occurs when the browser window is resized
scroll Occurs when the user scrolls in the specified element

Asynchronous JavaScript

Asynchronous programming allows you to execute code without blocking the main thread. This is essential for operations like fetching data from a server or reading a file.

Callbacks

A callback is a function passed as an argument to another function, which is then invoked inside the outer function.

function fetchData(callback) {
    setTimeout(function() {
        const data = { name: "John", age: 30 };
        callback(data);
    }, 1000);
}

fetchData(function(data) {
    console.log(data);  // { name: "John", age: 30 }
});

Promises

Promises provide a cleaner way to handle asynchronous operations. A promise represents a value that may not be available yet.

function fetchData() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            const data = { name: "John", age: 30 };
            // Simulate successful operation
            resolve(data);
            // Or simulate an error
            // reject(new Error("Failed to fetch data"));
        }, 1000);
    });
}

fetchData()
    .then(function(data) {
        console.log(data);  // { name: "John", age: 30 }
        return data.name;
    })
    .then(function(name) {
        console.log(name);  // "John"
    })
    .catch(function(error) {
        console.error(error);
    })
    .finally(function() {
        console.log("Operation completed");
    });

Async/Await

Async/await is a syntactic sugar on top of promises, making asynchronous code look and behave more like synchronous code.

async function fetchData() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            const data = { name: "John", age: 30 };
            resolve(data);
        }, 1000);
    });
}

async function displayData() {
    try {
        const data = await fetchData();
        console.log(data);  // { name: "John", age: 30 }
        
        const name = data.name;
        console.log(name);  // "John"
    } catch (error) {
        console.error(error);
    } finally {
        console.log("Operation completed");
    }
}

displayData();

Fetch API

The Fetch API provides a modern interface for fetching resources across the network.

// Using promises
fetch('https://api.example.com/data')
    .then(response => {
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        return response.json();
    })
    .then(data => {
        console.log(data);
    })
    .catch(error => {
        console.error('Fetch error:', error);
    });

// Using async/await
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Fetch error:', error);
    }
}

fetchData();

ES6+ Features

ECMAScript 2015 (ES6) and later versions introduced many new features to JavaScript. Here are some of the most important ones:

let and const

Block-scoped variables and constants.

// let - block-scoped variable
let x = 10;
if (true) {
    let x = 20;  // Different variable
    console.log(x);  // 20
}
console.log(x);  // 10

// const - block-scoped constant
const PI = 3.14159;
// PI = 3.14;  // Error: Assignment to constant variable

Arrow Functions

A shorter syntax for writing function expressions.

// Traditional function expression
let add = function(a, b) {
    return a + b;
};

// Arrow function
let add = (a, b) => a + b;

// Arrow function with no parameters
let sayHello = () => "Hello";

// Arrow function with one parameter
let double = x => x * 2;

Template Literals

String literals that allow embedded expressions.

let name = "John";
let age = 30;

// Traditional string concatenation
let message = "My name is " + name + " and I am " + age + " years old.";

// Template literal
let message = `My name is ${name} and I am ${age} years old.`;

// Multi-line strings
let multiLine = `This is a
multi-line
string`;

Destructuring Assignment

A syntax that allows you to extract data from arrays or objects into distinct variables.

// Array destructuring
let [a, b] = [1, 2];
console.log(a);  // 1
console.log(b);  // 2

// Object destructuring
let person = { name: "John", age: 30 };
let { name, age } = person;
console.log(name);  // "John"
console.log(age);  // 30

// With different variable names
let { name: personName, age: personAge } = person;
console.log(personName);  // "John"
console.log(personAge);  // 30

// Default values
let { name, age, gender = "unknown" } = person;
console.log(gender);  // "unknown"

Spread and Rest Operators

The spread operator (...) allows an iterable to be expanded in places where zero or more arguments or elements are expected. The rest operator collects multiple elements into a single array.

// Spread operator with arrays
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let combined = [...arr1, ...arr2];  // [1, 2, 3, 4, 5, 6]

// Spread operator with objects
let obj1 = { a: 1, b: 2 };
let obj2 = { c: 3, d: 4 };
let combined = { ...obj1, ...obj2 };  // { a: 1, b: 2, c: 3, d: 4 }

// Rest operator in function parameters
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5));  // 15

// Rest operator in destructuring
let [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first);  // 1
console.log(rest);  // [2, 3, 4, 5]

Default Parameters

Default function parameters allow parameters to be initialized with default values if no value or undefined is passed.

function greet(name = "Guest", greeting = "Hello") {
    return `${greeting}, ${name}!`;
}

console.log(greet());  // "Hello, Guest!"
console.log(greet("John"));  // "Hello, John!"
console.log(greet("John", "Hi"));  // "Hi, John!"

Classes

ES6 introduced a class syntax that provides a clearer and more concise way to create objects and deal with inheritance.

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    greet() {
        return `Hello, my name is ${this.name}`;
    }
    
    // Static method
    static createAnonymous() {
        return new Person("Anonymous", 0);
    }
    
    // Getter
    get ageInMonths() {
        return this.age * 12;
    }
    
    // Setter
    set ageInMonths(months) {
        this.age = months / 12;
    }
}

let person = new Person("John", 30);
console.log(person.greet());  // "Hello, my name is John"

let anonymous = Person.createAnonymous();
console.log(anonymous.name);  // "Anonymous"

console.log(person.ageInMonths);  // 360
person.ageInMonths = 360;
console.log(person.age);  // 30

Modules

ES6 modules allow you to split your code into separate files, making it more maintainable.

// math.js
export function add(a, b) {
    return a + b;
}

export function subtract(a, b) {
    return a - b;
}

export const PI = 3.14159;

// main.js
import { add, subtract, PI } from './math.js';
console.log(add(5, 3));  // 8
console.log(subtract(5, 3));  // 2
console.log(PI);  // 3.14159

// Import everything
import * as math from './math.js';
console.log(math.add(5, 3));  // 8

// Default export
// utils.js
export default function() {
    console.log("Default export");
}

// main.js
import myFunction from './utils.js';
myFunction();  // "Default export"

JavaScript Best Practices

Following best practices ensures that your JavaScript code is maintainable, efficient, and performs well.

Code Style

Performance

Security

Error Handling

// Good error handling
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Fetch error:', error);
        // Handle the error appropriately
        throw error;  // Re-throw if needed
    } finally {
        // Cleanup code
    }
}

Testing

Modern JavaScript

Debugging

Additional Resources

Here are some valuable resources for learning more about JavaScript:

Documentation

Tutorials and Guides

Tools

Books