JavaScript

Complete Guide: ES6+ Features, DOM, Async Programming & More

Basics & Variables

Variable Declaration

KeywordScopeHoistingReassignRedeclare
varFunctionYes (undefined)
letBlockYes (TDZ)
constBlockYes (TDZ)
JavaScript
// Variables
var oldWay = "function scoped";
let modern = "block scoped";
const constant = "cannot reassign";

// Data Types
let str = "Hello";           // String
let num = 42;                // Number
let float = 3.14;            // Number
let bool = true;             // Boolean
let nothing = null;          // Null
let notDefined;              // Undefined
let symbol = Symbol("id");   // Symbol
let bigInt = 9007199254740991n; // BigInt

// Type checking
console.log(typeof str);     // "string"
console.log(typeof null);    // "object" (JS quirk)

// Type conversion
let strNum = "123";
console.log(Number(strNum)); // 123
console.log(parseInt("123px")); // 123
console.log(String(123));    // "123"
console.log(Boolean(1));     // true

// Truthy and Falsy
// Falsy: false, 0, "", null, undefined, NaN
// Truthy: everything else

Functions

JavaScript
// Function Declaration (hoisted)
function greet(name) {
    return `Hello, ${name}!`;
}

// Function Expression (not hoisted)
const greet2 = function(name) {
    return `Hello, ${name}!`;
};

// Arrow Function (ES6)
const greet3 = (name) => `Hello, ${name}!`;
const greet4 = name => `Hello, ${name}!`;  // Single param
const add = (a, b) => a + b;               // Implicit return

// Arrow with body
const greet5 = (name) => {
    const greeting = `Hello, ${name}!`;
    return greeting;
};

// Default Parameters
const greet6 = (name = "Guest") => `Hello, ${name}!`;

// Rest Parameters
const sum = (...numbers) => numbers.reduce((a, b) => a + b, 0);
console.log(sum(1, 2, 3, 4)); // 10

// Spread Operator
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

// Higher Order Functions
const numbers = [1, 2, 3, 4, 5];

// map - transform each element
const doubled = numbers.map(n => n * 2);

// filter - keep elements that pass test
const evens = numbers.filter(n => n % 2 === 0);

// reduce - accumulate to single value
const total = numbers.reduce((acc, n) => acc + n, 0);

// forEach - iterate without return
numbers.forEach(n => console.log(n));

// find - first element that passes test
const found = numbers.find(n => n > 3);

// some - at least one passes
const hasEven = numbers.some(n => n % 2 === 0); // true

// every - all pass
const allPositive = numbers.every(n => n > 0); // true

Arrays & Methods

JavaScript
// Array Creation
const arr = [1, 2, 3];
const arr2 = new Array(5);  // Array of 5 undefined
const arr3 = Array.from("hello"); // ['h','e','l','l','o']
const arr4 = Array.of(1, 2, 3);   // [1, 2, 3]

// Accessing
arr[0];           // 1
arr.at(-1);       // 3 (last element, ES2022)

// Modifying (mutating)
arr.push(4);      // Add to end
arr.pop();        // Remove from end
arr.unshift(0);   // Add to beginning
arr.shift();      // Remove from beginning
arr.splice(1, 2); // Remove 2 items starting at index 1
arr.splice(1, 0, 'a', 'b'); // Insert at index 1

// Non-mutating
const sliced = arr.slice(1, 3);  // Elements from index 1 to 2
const joined = arr.join(", ");   // "1, 2, 3"
const concated = arr.concat([4, 5]);

// Searching
arr.indexOf(2);        // 1 (-1 if not found)
arr.includes(2);       // true
arr.findIndex(x => x > 1); // Index of first match

// Sorting
const nums = [3, 1, 4, 1, 5];
nums.sort((a, b) => a - b);  // Ascending
nums.sort((a, b) => b - a);  // Descending
nums.reverse();

// Destructuring
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first = 1, second = 2, rest = [3, 4, 5]

// Flat and FlatMap
const nested = [1, [2, [3, [4]]]];
nested.flat();     // [1, 2, [3, [4]]]
nested.flat(2);    // [1, 2, 3, [4]]
nested.flat(Infinity); // [1, 2, 3, 4]

const arr5 = [1, 2, 3];
arr5.flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]

Objects

JavaScript
// Object Creation
const person = {
    name: "Alice",
    age: 25,
    greet() {
        return `Hello, I'm ${this.name}`;
    }
};

// Shorthand properties (ES6)
const name = "Bob";
const age = 30;
const bob = { name, age };  // { name: "Bob", age: 30 }

// Computed properties
const key = "dynamic";
const obj = {
    [key]: "value",
    [`${key}_2`]: "value2"
};

// Access and modify
person.name;           // "Alice"
person["name"];        // "Alice"
person.job = "Developer"; // Add property
delete person.age;     // Delete property

// Object methods
Object.keys(person);   // ["name", "age", "greet"]
Object.values(person); // ["Alice", 25, function]
Object.entries(person); // [["name", "Alice"], ...]

// Destructuring
const { name: userName, age: userAge } = person;

// Spread operator
const newPerson = { ...person, city: "NYC" };

// Object.assign
const merged = Object.assign({}, obj1, obj2);

// Optional chaining
const user = { profile: { name: "John" } };
console.log(user?.profile?.name); // "John"
console.log(user?.address?.city); // undefined (no error)

// Nullish coalescing
const value = null ?? "default"; // "default"
const value2 = 0 ?? "default";   // 0

// Object freeze/seal
Object.freeze(obj);  // No changes allowed
Object.seal(obj);    // No add/delete, but can modify

ES6+ Features

JavaScript
// Template Literals
const name = "Alice";
const greeting = `Hello, ${name}!`;
const multiline = `
    This is
    multiline
`;

// Classes
class Person {
    #privateField = "secret";  // Private field (ES2022)
    static count = 0;          // Static property
    
    constructor(name, age) {
        this.name = name;
        this.age = age;
        Person.count++;
    }
    
    greet() {
        return `Hello, I'm ${this.name}`;
    }
    
    get info() {
        return `${this.name}, ${this.age}`;
    }
    
    set info(value) {
        [this.name, this.age] = value.split(", ");
    }
    
    static createAnonymous() {
        return new Person("Anonymous", 0);
    }
}

class Employee extends Person {
    constructor(name, age, salary) {
        super(name, age);
        this.salary = salary;
    }
    
    greet() {
        return `${super.greet()}, I work here`;
    }
}

// Map - key-value with any key type
const map = new Map();
map.set("key", "value");
map.set(123, "number key");
map.set({}, "object key");
map.get("key");     // "value"
map.has("key");     // true
map.delete("key");
map.size;           // number of entries

// Set - unique values
const set = new Set([1, 2, 2, 3]);
set.add(4);
set.has(2);         // true
set.delete(1);
set.size;           // 3

// Symbol
const sym = Symbol("description");
const obj = {
    [sym]: "symbol value"
};

// for...of (iterables)
for (const item of [1, 2, 3]) {
    console.log(item);
}

for (const [key, value] of map) {
    console.log(key, value);
}

Async JavaScript

JavaScript
// Callbacks
function fetchData(callback) {
    setTimeout(() => {
        callback("Data received");
    }, 1000);
}

// Promises
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Success!");
        // reject(new Error("Failed"));
    }, 1000);
});

promise
    .then(result => console.log(result))
    .catch(error => console.error(error))
    .finally(() => console.log("Done"));

// Promise methods
Promise.all([p1, p2, p3])       // All must resolve
    .then(results => {});

Promise.allSettled([p1, p2])    // Wait for all to settle
    .then(results => {});

Promise.race([p1, p2])          // First to settle
    .then(result => {});

Promise.any([p1, p2])           // First to fulfill
    .then(result => {});

// Async/Await
async function fetchUser() {
    try {
        const response = await fetch('/api/user');
        const user = await response.json();
        return user;
    } catch (error) {
        console.error("Error:", error);
    }
}

// Parallel async operations
async function fetchAll() {
    const [users, posts] = await Promise.all([
        fetch('/api/users').then(r => r.json()),
        fetch('/api/posts').then(r => r.json())
    ]);
    return { users, posts };
}

// Top-level await (ES2022, modules only)
const data = await fetch('/api/data').then(r => r.json());

// Fetch API
fetch('https://api.example.com/data', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({ name: 'John' })
})
.then(response => response.json())
.then(data => console.log(data));

DOM Manipulation

JavaScript
// Selecting Elements
const el = document.getElementById('myId');
const el2 = document.querySelector('.myClass');
const els = document.querySelectorAll('div.item');
const els2 = document.getElementsByClassName('myClass');

// Creating Elements
const div = document.createElement('div');
div.textContent = 'Hello';
div.innerHTML = '<span>HTML content</span>';
div.id = 'newDiv';
div.className = 'container';
div.classList.add('active');
div.classList.remove('inactive');
div.classList.toggle('visible');
div.setAttribute('data-id', '123');

// Adding to DOM
document.body.appendChild(div);
parent.insertBefore(newEl, referenceEl);
parent.replaceChild(newEl, oldEl);
el.remove();

// Styles
el.style.color = 'red';
el.style.backgroundColor = 'blue';
const styles = getComputedStyle(el);

// Events
el.addEventListener('click', (e) => {
    e.preventDefault();
    e.stopPropagation();
    console.log(e.target);
});

// Event Delegation
document.getElementById('list').addEventListener('click', (e) => {
    if (e.target.tagName === 'LI') {
        console.log(e.target.textContent);
    }
});

// Common Events
// click, dblclick, mouseenter, mouseleave
// keydown, keyup, keypress
// submit, change, input, focus, blur
// load, DOMContentLoaded, scroll, resize

// Form handling
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
    e.preventDefault();
    const formData = new FormData(form);
    const data = Object.fromEntries(formData);
});

// Local Storage
localStorage.setItem('key', JSON.stringify({ name: 'John' }));
const data = JSON.parse(localStorage.getItem('key'));
localStorage.removeItem('key');
localStorage.clear();

Closures & Scope

JavaScript
// Closure - function remembers its lexical scope
function createCounter() {
    let count = 0;  // Private variable
    
    return {
        increment: () => ++count,
        decrement: () => --count,
        getCount: () => count
    };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount();  // 2

// Practical use: Module pattern
const calculator = (function() {
    let result = 0;
    
    return {
        add: (x) => result += x,
        subtract: (x) => result -= x,
        getResult: () => result
    };
})();

// Closure in loops (common interview question)
// Problem
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 1000);
}
// Logs: 3, 3, 3 (all same)

// Solution 1: let
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 1000);
}
// Logs: 0, 1, 2

// Solution 2: IIFE
for (var i = 0; i < 3; i++) {
    ((j) => {
        setTimeout(() => console.log(j), 1000);
    })(i);
}

// Scope chain
const global = "global";

function outer() {
    const outer = "outer";
    
    function inner() {
        const inner = "inner";
        console.log(global, outer, inner);  // All accessible
    }
    
    inner();
}

// Hoisting
console.log(x);  // undefined (var hoisted)
var x = 5;

console.log(y);  // ReferenceError (TDZ)
let y = 5;

this Keyword

JavaScript
// this in different contexts

// 1. Global context
console.log(this);  // window (browser) / global (Node)

// 2. Object method
const obj = {
    name: "Alice",
    greet() {
        console.log(this.name);  // "Alice"
    }
};

// 3. Regular function
function regular() {
    console.log(this);  // window/global (non-strict)
                        // undefined (strict mode)
}

// 4. Arrow function (lexical this)
const arrow = () => {
    console.log(this);  // Inherits from enclosing scope
};

const obj2 = {
    name: "Bob",
    greet: () => {
        console.log(this.name);  // undefined! (lexical this)
    },
    greetCorrect() {
        const inner = () => {
            console.log(this.name);  // "Bob" (inherits from greetCorrect)
        };
        inner();
    }
};

// 5. Event handlers
button.addEventListener('click', function() {
    console.log(this);  // The button element
});

button.addEventListener('click', () => {
    console.log(this);  // Lexical this (not the button!)
});

// Explicit binding
function greet() {
    console.log(`Hello, ${this.name}`);
}

const person = { name: "Alice" };

// call - invoke immediately with args
greet.call(person);  // "Hello, Alice"
greet.call(person, arg1, arg2);

// apply - same as call, but args as array
greet.apply(person, [arg1, arg2]);

// bind - returns new function with bound this
const boundGreet = greet.bind(person);
boundGreet();  // "Hello, Alice"

// 6. Constructor
function Person(name) {
    this.name = name;  // this = new object
}
const p = new Person("Alice");
Interview Key Points:
• Arrow functions don't have their own this
bind returns a new function
call and apply invoke immediately
• In callbacks, this can be unpredictable - use arrow functions or bind