Skip to the content.

« Go To Home

JavaScript Concept All In One


Table of Content


Introduction to JavaScript

Property of Javascript

Loosely Typed

JavaScript is a loosely typed language, meaning variables can hold values of any type, and types can change dynamically.

Example:

let data = 42; // Initially a number
data = 'Hello'; // Now a string
console.log(data); // Output: Hello

Explanation:

You don’t need to declare variable types explicitly. This flexibility allows faster development but requires careful handling to avoid type-related bugs.


Object-Oriented Programming

Object-Oriented Programming (OOP) organizes code into objects that encapsulate data and behaviors. OOP concepts include Classes, Objects, Inheritance, Encapsulation, Polymorphism, and Abstraction.

Example: Class and Object

class Car {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }
  getDetails() {
    return `${this.brand} ${this.model}`;
  }
}

const myCar = new Car('Tesla', 'Model S');
console.log(myCar.getDetails()); // Output: Tesla Model S

Benefits of OOP:


Functional Programming

Functional programming treats computation as the evaluation of mathematical functions and avoids changing state or mutable data.

Example: Pure Function

const add = (a, b) => a + b;
console.log(add(2, 3)); // Output: 5

Explanation:

The output depends only on the input, ensuring predictable results without side effects.


History and Purpose of JavaScript

JavaScript in Web Development


Variables and Data Types

Declaring Variables

Primitive Types

Primitive types include number, string, boolean, null, undefined, and symbol.

Value Types vs Reference Types

Value types (e.g., numbers, strings) store data directly, while reference types (e.g., objects) store memory references.

Object destructuring allows you to extract properties from objects and bind them to variables.

Example: (Pass by Reference): Here var obj gets assigned to memory reference of user so, it changes the original value.

const user = { name: 'Alice', age: 25 };
const obj = user;
obj.age = 30;
console.log(user); // Output: { name: 'Alice', age: 30 }

(Pass by Value): Here a copy gets created due to object destructuring and obj gets assigned to value of user.

const user = { name: 'Alice', age: 25 };
const obj = { ...user };
obj.age = 30;
console.log(user); // Output:{ name: 'Alice', age: 25 }

Example:

let a = 10;
let b = a; // Value copy

let obj1 = { key: 'value' };
let obj2 = obj1; // Reference copy

Type Coercion: Implicit vs Explicit


Nominal, Structural, and Duck Typinga



Operators

Arithmetic Operators

Comparison Operators (== vs === vs typeof)

Example:

console.log(2 == '2'); // Output: true
console.log(2 === '2'); // Output: false
console.log(typeof 42); // Output: number

Bitwise Operators

Logical Operators


Expression vs Statement

According to the ECMAScript specification, expressions produce a value, and statements are instructions to perform an action, such as variable assignment or control flow. Function declarations are hoisted and can be called before they are defined in the code, while function expressions are not hoisted and must be defined before being invoked.


Control Flow

If-Else Statements

Switch Statements

Looping Statements (For, While, Do-While)

Functions

Function Declarations and Expressions

Function Scope, Block Scope, and Lexical Scope

Function Scope: Variables declared with var are function-scoped, meaning they are accessible throughout the function where they are defined. This scope isolates variables from being accessed outside of the function where they are declared.

Example:

function demoVar() {
  if (true) {
    var x = 10;
  }
  console.log(x); // Output: 10
}

Block Scope: Variables declared with let and const are block-scoped, meaning they are only accessible within the block where they are defined.

Example:

function demoLet() {
  if (true) {
    let y = 20;
    const z = 30;
    console.log(y, z); // Output: 20 30
  }
  // console.log(y, z); // ReferenceError
}

Lexical Scope: Lexical scope means that the scope of a variable is determined by its position in the source code and nested scopes have access to variables from their parent scope.

Example:

function outer() {
  let a = 5;
  function inner() {
    console.log(a); // Output: 5
  }
  inner();
}
outer();

Closures: A closure is formed when an inner function “remembers” variables from its outer function even after the outer function has executed.

Example:

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

const counter = createCounter();
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2

Pros and Cons of Closures

Pros:

  1. Enables data encapsulation and privacy.
  2. Maintains state in functions.
  3. Avoids global variables.

Cons:

  1. Overuse can lead to memory leaks.
  2. Debugging closures can be tricky.

What if JavaScript had no closures?

Without closures, maintaining state and creating private variables would require alternative patterns, such as using objects or classes.


Advanced Closure Example

Question: Evaluate and explain the output.

function outer() {
  let count = 0;
  return function inner() {
    count++;
    console.log(count);
  };
}

const fn = outer();
fn(); // Output: 1
fn(); // Output: 2
fn(); // Output: 3

Explanation:

Each invocation of fn updates the count variable in the closure created by the outer function.


IIFE (Immediately Invoked Function Expressions)

High-Order Functions

Recursion


Scope and Closures

Variable Scope (Global, Local)

Hoisting in JavaScript

Hoisting in JavaScript

Hoisting is a JavaScript mechanism where variable and function declarations are moved to the top of their scope during the compile phase. This means variables and functions can be used in code before their declaration without causing a runtime error, though their values might not yet be assigned.


Example of Hoisting

console.log(x); // Output: undefined
var x = 5;

sayHello(); // Output: "Hello!"
function sayHello() {
  console.log('Hello!');
}

Explanation:

  1. Variable x is hoisted, but only its declaration, not its initialization. Therefore, console.log(x) outputs undefined.
  2. The function sayHello is hoisted entirely, so it can be called before its definition.

Temporal Dead Zone

The Temporal Dead Zone (TDZ) refers to the time between entering the scope of a variable declared with let or const and its initialization.

Example:

console.log(a); // ReferenceError
let a = 10;

Key Notes:

Example: Hoisting with Variables

console.log(a); // Output: undefined
var a = 10;

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;

Expected Outputs in Sample Programs

  1. Simple Example:
    console.log(a); // undefined
    var a = 10;
    console.log(a); // 10
    
  2. Order of Execution:
    console.log('Second greetings');
    console.log('First greetings');
    console.log('First greetings');
    
  3. Variable Hoisting:

    let variable_1 = 35;
    var variable_2 = 10;
    const variable_3 = 15;
    
    console.log(variable_1); // 35
    console.log(variable_2); // 10
    console.log(variable_3); // 15
    console.log(variable_4); // ReferenceError: variable_4 is not defined
    

Closures and Practical Use Cases


Call Stack and Execution Context

The call stack manages execution contexts in JavaScript. The call stack is a mechanism that the JavaScript interpreter uses to keep track of function execution within a program. In JavaScript, functions are executed in the order they are called. The call stack follows the Last In, First Out (LIFO) principle, meaning that the last function pushed onto the stack is the first one to be executed.

According to the ECMAScript specification, the call stack is defined as part of the execution context. Whenever a function is called, a new execution context is created and placed at the top of the stack. Once the function completes, its execution context is removed from the stack, and control returns to the previous context. This helps manage synchronous code execution, as each function call must complete before the next one can begin.

Example:

function first() {
  second();
}
function second() {
  console.log('In second function');
}
first(); // Output: In second function

Understanding the Call Stack

Message Queue and Event Loop

The Event Loop is a critical part of JavaScript’s concurrency model, ensuring non-blocking behavior by processing tasks in an asynchronous manner. JavaScript uses the Event Loop to handle asynchronous operations.

Example:

console.log('Start');
setTimeout(() => console.log('Async Task'), 0);
console.log('End');
// Output: Start, End, Async Task

Microtasks and Macrotasks

Context

In JavaScript, context refers to the value of this within a function or object. It determines which object or value this is referring to when the code is executed. Understanding context is key to how JavaScript handles object methods, function invocations, and event handling.

Types of Context in JavaScript:

  1. Global Context (or Window Context in browsers):

    • When JavaScript is executed outside of any function or object, this refers to the global object.
    • In the browser, the global object is window.
    • In Node.js, the global object is global.

    Example:

    console.log(this); // In browsers, this will log the window object
    
  2. Function Context:

    • Inside a function, the value of this is determined by how the function is called.
    • If the function is called as a method of an object, this refers to the object.
    • If the function is called without an object (i.e., as a regular function call), this refers to the global object in non-strict mode, or undefined in strict mode.

    Example:

    function greet() {
      console.log(this);
    }
    greet(); // In non-strict mode, this will refer to the global object (window in browsers)
    
    const person = {
      name: 'Alice',
      greet: function () {
        console.log(this.name);
      },
    };
    person.greet(); // this refers to the 'person' object, so it will log "Alice"
    
  3. Object Context:

    • When a method is invoked as part of an object (e.g., obj.method()), this refers to the object the method is a part of.

    Example:

    const obj = {
      name: 'Bob',
      greet: function () {
        console.log(this.name);
      },
    };
    obj.greet(); // "Bob" because 'this' refers to 'obj'
    
  4. Class Context:

    • In ES6 classes, this refers to the instance of the class.

    Example:

    class Person {
      constructor(name) {
        this.name = name;
      }
      greet() {
        console.log(this.name);
      }
    }
    const person = new Person('Charlie');
    person.greet(); // "Charlie" because 'this' refers to the instance of the class
    
  5. Arrow Functions Context:

    • Arrow functions have lexical scoping for this, meaning this is inherited from the surrounding non-arrow function or context where the arrow function was created.
    • They don’t have their own this like regular functions do.

    Example:

    const obj = {
      name: 'David',
      greet: function () {
        const arrowFunc = () => {
          console.log(this.name);
        };
        arrowFunc();
      },
    };
    obj.greet(); // "David", because 'this' inside the arrow function refers to 'obj'
    
  6. Explicit Binding (Using .call(), .apply(), or .bind()):

    • JavaScript provides methods to explicitly set the value of this using .call(), .apply(), or .bind().

    Example:

    function greet() {
      console.log(this.name);
    }
    
    const person = { name: 'Emma' };
    greet.call(person); // "Emma" because we explicitly set 'this' to 'person'
    

Summary:

⬆ Back to Top


Asynchronous JavaScript

setTimeout, setInterval, and requestAnimationFrame

  1. setTimeout: Runs a function after a delay.
    setTimeout(() => console.log('After 1 second'), 1000);
    
  2. setInterval: Repeats execution at fixed intervals.
    setInterval(() => console.log('Every second'), 1000);
    
  3. requestAnimationFrame: Optimized for animations.
    const animate = () => {
      console.log('Animating');
      requestAnimationFrame(animate);
    };
    requestAnimationFrame(animate);
    

Promises and Fetch API

Async/Await

Callback Functions and Callback Hell


Objects and Arrays

Object Literals and Properties

Methods and Prototype Inheritance

Object.create and Object.assign

Arrays: Creating, Accessing, and Array Methods (map, reduce, filter)


DOM and Layout Trees

DOM Manipulation: Selecting and Modifying Elements

Layout Trees and Reflow

Event Propagation: Bubbling and Capturing


ES6 and Beyond

Arrow Functions

Template Literals

Destructuring Assignments

Modules (Import and Export Syntax)


Object-Oriented Programming (OOP)

Factories and Classes

Class Definitions and Instances

Prototype Inheritance and Prototype Chain

this, call, apply, and bind

new, Constructor, instanceof, and Instances

Inheritance, Polymorphism, and Code Reuse


Functional Programming

Pure Functions and Side Effects

State Mutation and Event Propagation

Partial Applications and Currying

Compose and Pipe


Data Structures and Algorithms

Arrays, Maps, Sets, and Typed Arrays

Array Buffers

Collections and Generators

Expensive Operations and Big O Notation

Algorithms Basics


JavaScript Engines and Performance

JavaScript Engines (V8, SpiderMonkey, etc.)

Performance Optimization Techniques

Reducing DOM Manipulation


Error Handling and Debugging

Error Types and Handling

Debugging Techniques and Tools


Web Storage

LocalStorage and SessionStorage


API Integration

AJAX (Asynchronous JavaScript and XML)

RESTful API Concepts

Fetch API


Design Patterns

Singleton Pattern

Factory Pattern

Observer Pattern


Regular Expressions

Pattern Matching and Manipulation

Regular Expression Methods and Modifiers


Event Handling

Event Listeners and the Event Object

Event Delegation


Clean Code and Best Practices

Writing Readable and Maintainable Code

Reducing Technical Debt

Code Reviews and Refactoring


Security Considerations in JavaScript

Cross-Site Scripting (XSS)

Cross-Site Request Forgery (CSRF)


Server-Side JavaScript

Introduction to Node.js


Data Visualization with JavaScript Libraries

D3.js for Data Visualization

Chart.js for Creating Charts

⬆ Back to Top


Window Object in JavaScript

The window object in JavaScript represents the global execution context in web browsers. It is the top-level object for interacting with the browser and provides numerous properties, methods, and events.

Instance Properties

The window object exposes many properties that give insights into the browser and the environment. These properties can be used to manipulate or retrieve information about the browser state.

Example: Accessing a property

console.log(window.location.href); // Outputs the current URL of the page

Explanation:

The location property is an object that contains information about the current URL. You can use it to navigate to a new URL, reload the page, or retrieve URL components.

Instance Methods

The window object provides several methods for interacting with the browser. These methods allow tasks such as displaying dialogs, controlling timers, or navigating through browsing history.

Example: Using alert method

window.alert('Hello, World!'); // Displays an alert dialog with the message

Example: Using setTimeout method

window.setTimeout(function () {
  console.log('This will execute after 2 seconds');
}, 2000);

Explanation:

Events

The window object has numerous events that allow interaction with user actions or browser state changes. These events can be used to enhance the user experience and respond dynamically to changes.

Example: Listening to a custom event

window.addEventListener('click', function () {
  console.log('Window clicked!');
});

Types of Events in window

  1. Load Event

    • Triggered when the page and all its resources (images, scripts, etc.) are fully loaded.
    • Property: onload

    Example:

    window.onload = function () {
      console.log('Page fully loaded');
    };
    
  2. Resize Event

    • Triggered when the browser window is resized.
    • Property: onresize

    Example:

    window.onresize = function () {
      console.log(
        'Window resized to',
        window.innerWidth,
        'x',
        window.innerHeight
      );
    };
    
  3. Scroll Event

    • Triggered when the user scrolls the page.
    • Property: onscroll

    Example:

    window.onscroll = function () {
      console.log('Page scrolled to', window.scrollY);
    };
    
  4. Error Event

    • Triggered when a JavaScript error occurs.
    • Property: onerror

    Example:

    window.onerror = function (message, source, lineno, colno, error) {
      console.error(`Error: ${message} at ${source}:${lineno}:${colno}`);
    };
    
  5. BeforeUnload Event

    • Triggered when the user is about to leave the page.
    • Property: onbeforeunload

    Example:

    window.onbeforeunload = function () {
      return 'Are you sure you want to leave this page?';
    };
    

Inheritance

The window object is the global object in browsers and inherits from the Object prototype. Most global variables and functions automatically become properties of the window object.

Example:

console.log(window instanceof Object); // true

Explanation:

Other objects like document, navigator, and history are properties of the window object. This hierarchical structure enables easy access to browser functionality.


⬆ Back to Top


WeakSet and WeakMap

WeakSet and WeakMap hold weak references, allowing objects to be garbage-collected when not referenced elsewhere.

Example: WeakMap

let obj = {};
let weakMap = new WeakMap();
weakMap.set(obj, 'value');

console.log(weakMap.get(obj)); // Output: value

Generator Functions

Generator functions allow pausing and resuming execution using the yield keyword.

Example:

function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator();
console.log(gen.next().value); // Output: 1
console.log(gen.next().value); // Output: 2

Promises

Promises handle asynchronous operations and avoid callback hell.

Example:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Success!'), 1000);
});

promise.then(console.log); // Output: Success! (after 1 second)

Spread Operator

The spread operator (...) allows expanding iterable elements.

Example:

const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
console.log(newArr); // Output: [1, 2, 3, 4, 5]

Rest Parameter

The rest parameter gathers remaining elements into an array.

Example:

function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3)); // Output: 6

call, apply, and bind Methods

These methods control the this context in function invocation.

Example:

const obj = { x: 42 };
function showX() {
  console.log(this.x);
}

showX.call(obj); // Output: 42

IIFE, Modules, and Namespaces


JavaScript Engines

Engines like V8 (Chrome) convert JavaScript code into machine code for execution.


DOM and Layout Trees

The DOM (Document Object Model) represents the HTML structure. The layout tree applies styles for rendering.


Factories and Classes


this, call, apply, and bind

Example:

function greet() {
  console.log(`Hello, ${this.name}`);
}
greet.call({ name: 'John' }); // Output: Hello, John

map, reduce, filter, compose

  1. map: Transforms elements.
    [1, 2, 3].map((x) => x * 2); // [2, 4, 6]
    
  2. reduce: Accumulates values.
    [1, 2, 3].reduce((sum, x) => sum + x, 0); // 6
    
  3. filter: Filters elements.
    [1, 2, 3].filter((x) => x > 1); // [2, 3]
    
  4. compose: Combines functions.
    const add1 = (x) => x + 1;
    const multiply = (x) => x * 2;
    const compose = (f, g) => (x) => f(g(x));
    console.log(compose(add1, multiply)(5)); // 11
    

Closures

Functions retain access to variables in their lexical scope even after the outer function is executed.

Example:

function outer() {
  let count = 0;
  return function inner() {
    count++;
    console.log(count);
  };
}
const counter = outer();
counter(); // 1
counter(); // 2

Promises and async/await


Data Structures and Big O

Efficient data structures and algorithms improve performance.

Example:

const items = [1, 2, 3];
console.log(items.includes(2)); // O(n)

Design Patterns

Common reusable solutions, like Singleton or Observer.

Example (Singleton):

const Singleton = (function () {
  let instance;
  return {
    getInstance: () => instance || (instance = {}),
  };
})();


Difference Between Object Creation Using Object Constructor and Object.create()

JavaScript provides multiple ways to create objects, and understanding the distinction between these methods is crucial.

Object Constructor

function Person(name) {
  this.name = name;
}
const person1 = new Person('John');
console.log(person1.name); // Output: John

Object.create()

const prototypeObject = {
  greet() {
    console.log('Hello from prototype!');
  },
};
const person2 = Object.create(prototypeObject);
person2.name = 'Doe';
person2.greet(); // Output: Hello from prototype!

Key Differences

Aspect Object Constructor Object.create()
Inheritance From the constructor’s prototype. From the provided object directly.
Constructor Execution Runs the constructor function. No constructor is run.
Use Case For creating objects with methods and properties defined by a constructor. For prototypal inheritance or extending existing objects.

1. Object Creation and the delete Operator

Code Example

const prototypeObj = { height: 180 };
const person = Object.create(prototypeObj);

console.log(person.height); // Output: 180
delete person.height;
console.log(person.height); // Output: 180

2. Array Filter Polyfill

A polyfill mimics the behavior of existing methods for environments where they are not available.

Code Example

Array.prototype.myFilter = function (callback) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i], i, this)) {
      result.push(this[i]);
    }
  }
  return result;
};

// Usage
const arr = [1, 2, 3, 4];
const filtered = arr.myFilter((num) => num % 2 === 0);
console.log(filtered); // Output: [2, 4]

3. Using Math.max with Arrays

Since Math.max does not accept arrays directly, use the spread operator (...) to convert an array into individual arguments.

Code Example

const numbers = [10, 20, 30, 5];
const max = Math.max(...numbers);
console.log(max); // Output: 30

4. Swapping Variables Using Destructuring

Code Example

let a = 10,
  b = 20;
[a, b] = [b, a];
console.log(a, b); // Output: 20, 10

5. Executing Promises Sequentially

Using reduce to chain promises and ensure sequential execution.

Code Example

const arr = [1, 2, 3];

arr.reduce((prev, curr) => {
  return prev.then(
    () =>
      new Promise((resolve) => {
        setTimeout(() => {
          console.log(curr);
          resolve();
        }, 1000);
      })
  );
}, Promise.resolve());

6. Memoization

Code Example

function memoize(fn) {
  const cache = {};
  return function (...args) {
    const key = JSON.stringify(args);
    if (!cache[key]) {
      cache[key] = fn(...args);
    }
    return cache[key];
  };
}

// Usage
const slowFunction = (num) => num * 2;
const fastFunction = memoize(slowFunction);

console.log(fastFunction(5)); // Computed: 10
console.log(fastFunction(5)); // Cached: 10

7. Debounce Function

A debounce function ensures a function executes only after a specified time has elapsed since the last invocation.

Code Example

function debounce(func, delay) {
  let timeout;
  return function (...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), delay);
  };
}

// Usage
const log = debounce(() => console.log('Debounced!'), 1000);
log(); // Call multiple times within 1 second to see the effect.

8. Handling Callback Hell with Promises

Code Example

// Callback Hell
setTimeout(() => {
  console.log('Step 1');
  setTimeout(() => {
    console.log('Step 2');
    setTimeout(() => {
      console.log('Step 3');
    }, 1000);
  }, 1000);
}, 1000);

// With Promises
const delay = (ms, msg) =>
  new Promise((resolve) => {
    setTimeout(() => {
      console.log(msg);
      resolve();
    }, ms);
  });

delay(1000, 'Step 1')
  .then(() => delay(1000, 'Step 2'))
  .then(() => delay(1000, 'Step 3'));

9. Prototype vs __proto__

Code Example

function Person(name) {
  this.name = name;
}
Person.prototype.greet = function () {
  console.log(`Hello, ${this.name}`);
};

const john = new Person('John');
console.log(john.__proto__ === Person.prototype); // true

10. Real-World Usage of call, apply, bind

Code Example

function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

const user = { name: 'Alice' };

greet.call(user, 'Hello', '!'); // Output: Hello, Alice!
greet.apply(user, ['Hi', '.']); // Output: Hi, Alice.
const boundGreet = greet.bind(user, 'Hey');
boundGreet('?'); // Output: Hey, Alice?