JavaScript All Important Concepts

Asynchronous Programming

Callbacks

Callbacks are functions passed as arguments to other functions to be executed later, often after a task completes. This approach enables asynchronous operations, ensuring that subsequent code does not wait for the completion of the initial task.

setTimeout

setTimeout schedules a function to be executed after a specified delay.

console.log('Start');
setTimeout(() => {
    console.log('This message is delayed by 2 seconds');
}, 2000);
console.log('End');

Promises

Promises represent a value that may be available now, or in the future, or never. They enable more readable and manageable asynchronous code by providing .then() and .catch() methods for handling success and failure cases.

let promise = new Promise((resolve, reject) => {
    let success = true; // Change to false to see the rejection
    if(success) {
        resolve('Promise resolved!');
    } else {
        reject('Promise rejected!');
    }
});

promise.then((message) => {
    console.log(message);
}).catch((error) => {
    console.error(error);
});

Fetch

fetch is a modern, updated global function for making network requests. It returns a promise that is fulfilled once the response is available.

fetch('https://api.github.com')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));

Async & Await

async functions always return a promise. await pauses the execution of the async function, waiting for the promise to resolve.

async function fetchData() {
    try {
        let response = await fetch('https://api.github.com');
        let data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error:', error);
    }
}
fetchData();

Callback Hell

Callback Hell occurs when multiple nested callbacks make the code hard to read and maintain.

// Callback Hell Example
doSomething(function(result) {
    doSomethingElse(result, function(newResult) {
        doThirdThing(newResult, function(finalResult) {
            console.log('Got the final result: ' + finalResult);
        }, failureCallback);
    }, failureCallback);
}, failureCallback);

// Using Promises to Avoid Callback Hell
doSomething()
    .then(result => doSomethingElse(result))
    .then(newResult => doThirdThing(newResult))
    .then(finalResult => console.log('Got the final result: ' + finalResult))
    .catch(failureCallback);

// Using async/await to Avoid Callback Hell
async function asyncFunction() {
    try {
        let result = await doSomething();
        let newResult = await doSomethingElse(result);
        let finalResult = await doThirdThing(newResult);
        console.log('Got the final result: ' + finalResult);
    } catch (error) {
        failureCallback(error);
    }
}
asyncFunction();

JavaScript Closures and Scope

Scope

Scope determines the accessibility (visibility) of variables. JavaScript has two types of scope:

Closures

A closure is a function having access to the parent scope, even after the parent function has closed. Closures are created every time a function is created.

function outerFunction() {
    let outerVariable = 'I am outside!';
    
    function innerFunction() {
        console.log(outerVariable);
    }
    
    return innerFunction;
}

const inner = outerFunction();
inner(); // Outputs: I am outside!

Understanding the DOM

Nodes and Elements

The DOM tree consists of different types of nodes:

Accessing DOM Elements

You can access DOM elements using various methods:

Manipulating DOM Elements

You can modify DOM elements using JavaScript:

ES6+ Syntax and Features

Let and Const

In ES6, both let and const are used to declare variables, but they have key differences.

let x = 10;
if (true) {
  let x = 20; // Different variable than the outer x
}
console.log(x); // 10
const y = 5;
y = 10; // Error: Assignment to constant variable.

Arrow Functions

Arrow functions provide a shorter syntax and lexically bind this.

// Traditional Function
function add(a, b) {
    return a + b;
}

// Arrow Function
const add = (a, b) => a + b;

Template Literals

Template literals provide an easier way to create strings and include expressions.

let name = 'John';
let greeting = `Hello, ${name}!`;
console.log(greeting); // Outputs: Hello, John!

Destructuring

Destructuring allows unpacking values from arrays or properties from objects into distinct variables.

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

// Object Destructuring
let {name, age} = {name: 'John', age: 30};
console.log(name); // John
console.log(age); // 30

Rest and Spread Operators

The rest operator ... collects all remaining elements into an array, while the spread operator ... expands an array into individual elements.

// Rest Operator
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num);
}
console.log(sum(1, 2, 3)); // 6

// Spread Operator
let arr = [1, 2, 3];
let newArr = [...arr, 4, 5];
console.log(newArr); // [1, 2, 3, 4, 5]

Additional Resources