Hoisting in JavaScript is a behavior where variable and function declarations are moved to the top of their containing scope (script or function) during the compilation phase, before code execution. This means that you can use variables or call functions before they are declared in the code, though there are nuances depending on whether you're dealing with variables declared using var, let, const, or function declarations.
1. Function Declarations
Function declarations are hoisted and can be used before initialization.
2. Variable Declarations
var declarations are hoisted but its value is not initialized. Before initialization, the variable is 'undefined'.
let and const declarations are hoisted but they remain in a 'temporal dead zone' until the code execution reaches their line. Accessing them before initialization causes a ReferenceError.
3. Class Declarations
Similar to let and const, class declarations are hoisted but cannot be accessed before their definition due to the temporal dead zone.
Temporal Dead Zone (TDZ)
The Temporal Dead Zone (TDZ) in JavaScript refers to the period of time during which a variable declared with let, const, or a class is in scope but cannot be accessed or used. This happens between the beginning of the block scope where the variable is declared and the point where the variable is initialized.
Reference Error
When a variable declared using let, const, or a class object is accessed while it is still in the Temporal Dead Zone (TDZ), a ReferenceError is thrown.
Syntax Error
Attempting to redeclare a variable with let or don't assign a value to a const variable results in a SyntaxError.
Type Error
Attempting to reassign a variable with const results in a TypeError.
var, let, const, class, function and their scope
var
Function-scoped, accessible only within the function they are defined in or globally if declared outside a function, hoisted to the top of their scope and initialized with undefined. it can be used before their declaration which outputs undefined.
let & const
Block-scoped. Accessible only within the block (enclosed by {}) they are defined in. In browsers it is stored in 'Script' if declared in Global space.
class
Accessible only within the block it is defined in, Hoisted but not initialized, accessing it before declaration results in a ReferenceError (like let or const). It cannot be re-declared within the same scope.
function
Function-scoped if declared traditionally (using function), but block-scoped if defined inside a block with let or const using function expressions. Fully hoisted and can be used before declaration without errors. It can be re-declared if defined traditionally.
Accessing variables using window and this object in Global Scope
In JavaScript, when variables are declared in the global scope, they can be accessed using the window or this object, but their availability depends on how the variable is declared (var, let, or const).
Global Scope and the window Object
The window object is the global object in browsers. Any variable declared with var in the global scope automatically becomes a property of the window object.
using var
Variables declared with var in the global scope are attached to the window object, this in the global scope refers to the window object in browsers in non strict mode.
Using let or const
Variables declared with let or const do not become properties of the window object. These variables are stored in a separate scope managed by the JavaScript engine, calles Script scope in browsers.
Shadowing
Shadowing in JavaScript refers to the situation where a local variable or parameter has the same name as a variable in a broader scope (such as global or outer function scope). The local variable 'shadows' or 'overrides' the outer variable, making it inaccessible within the scope of the inner variable. This can occur with variables declared using var, let, or const, as well as function parameters.
var
let or const
Function Parameter Shadowing
Closure
A closure is the combination of a function bundled together with its 'Lexical Environment'.
Closures in Currying
Disadvantages of Closure
Increased Memory Usage
Variable Capturing in Loops
setTimeout with Closure
using var
using let
how not to use let
how to use var
Types of Functions
In JavaScript, functions are versatile and can be categorized based on their definition, usage, or scope. Below are the main types of functions in JavaScript:
Named Functions
Anonymous Functions
Arrow Functions
Function Expressions
Functions assigned to variables, either named or anonymous.
Immediately Invoked Function Expressions (IIFE)
Constructor Functions
Used to create objects, typically invoked with the 'new' keyword.
Generator Functions
Defined using function*, they can pause and resume execution using the yield keyword.
Async Functions
Introduced in ES8, they work with await for asynchronous operations.
Higher-Order Functions
Functions that take other functions as arguments or return functions.
First-Class Functions
Functions which can be assigned to variables, can be passed as arguments to other functions, can be returned from other functions, and can be stored in data structures.
Event Loop in JavaScript
The Event Loop is a mechanism that allows JavaScript to perform non-blocking operations by delegating tasks to the system while still running a single thread. It coordinates the execution of: Synchronous Code (executed on the call stack). Asynchronous Code (tasks in the callback queue or microtask queue).
Components of the Event Loop
1. Call Stack: Where synchronous code is executed, following a Last In, First Out (LIFO) order.
2. Web APIs: Asynchronous operations (e.g., setTimeout, DOM events, HTTP requests) are sent here and handled by the browser or Node.js environment.
3. Callback Queue (Task Queue): Holds callbacks for tasks like setTimeout and setInterval, processed on a First In, First Out (FIFO) basis.
4. Microtask Queue: Holds microtasks such as promises (.then), MutationObserver, and queueMicrotask. Always given priority over the callback queue.
Functional Programming
Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data. It emphasizes declarative code (what to do) over imperative code (how to do it).
Key Principles of Functional Programming
1. First-Class Functions: Functions are treated as first-class citizens, meaning they can be assigned to variables, passed as arguments to other functions, returned from other functions.
2. Pure Functions: Functions that always produce the same output for the same input. Have no side effects (do not modify external state).
3. Immutability: Data should not be mutated. Instead, new copies of data are created.
4. Higher-Order Functions: Functions that can take other functions as arguments. Return functions as results.
5. Declarative Code: Focuses on 'what to do' rather than 'how to do it.'
6. Avoid Side Effects: Functions should not affect or depend on external variables.
Key Functional Methods in JavaScript
1. map: Transforms an array by applying a function to each element.
2. filter: Filters elements based on a condition.
3. reduce: Reduces an array to a single value by applying a function cumulatively.
4. forEach: Executes a provided function once for each array element (non-functional, doesn't return a new array).
Callback Hell
Callback hell refers to the situation where multiple nested callbacks are used to handle asynchronous operations, making the code difficult to read, debug, and maintain. This often results in a 'pyramid of doom' structure, where the code becomes deeply nested.
Inversion of Control
Inversion of control (IoC) refers to a design principle where control over the execution of a function is given to another component or framework. This can be seen in callback functions, where the timing and invocation of the callback are controlled by an external function or library.
Promises in JavaScript
A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It allows developers to handle asynchronous operations in a more readable and manageable way compared to callbacks.
States of a Promise
1. Pending: The initial state, neither fulfilled nor rejected.
2. Fulfilled: The operation completed successfully.
3. Rejected: The operation failed.
Once a promise is resolved (fulfilled or rejected), it becomes immutable and cannot change states.
Chaining Promises
Promises can be chained to perform multiple asynchronous operations in sequence.
Any no. of then, catch and finally can be called in chaining after each statment. 'then' after catch will always will called.
Promise Methods
1. Promise.resolve(value): Creates a promise that is resolved with the given value.
2. Promise.reject(reason): Creates a promise that is rejected with the given reason.
3. Promise.all(promises): Waits for all promises in the iterable to be fulfilled or for any to be rejected.
4. Promise.race(promises): Returns a promise that resolves or rejects as soon as one of the promises settles.
5. Promise.allSettled(promises): Waits for all promises to settle (fulfilled or rejected) and returns their results.
6. Promise.any(promises): Resolves as soon as any of the promises in the iterable is fulfilled. Rejects only if all the promises are rejected, in which case it throws an AggregateError containing all the rejection reasons.
All Promises Rejected
Async/Await
Async/Await is a syntactic sugar built on top of JavaScript Promises. It allows developers to write asynchronous code in a more synchronous and readable way.
Async Functions:
Declared with the async keyword. Always return a Promise. The value returned by an async function is automatically wrapped in a Promise.
Await Keyword:
Can only be used inside async functions. Pauses the execution of the async function until the Promise is resolved. Resumes execution with the resolved value of the Promise. If the Promise is rejected, it throws an error that can be caught using try...catch.
Sequential Execution
Parallel Execution
Practical Use Case: Fetching Data
this keyword mystries
The this keyword in JavaScript is a dynamic context-dependent reference that points to the object that is currently executing a function. Its value depends on how and where the function is called.
1. Global Context
In the global scope this refers to the global object (window in browsers or global in Node.js).
2. Inside a Function
Non-Strict Mode: this refers to the global object. Strict Mode: this is undefined.
3. Inside a Method
When a method is called on an object, this refers to the object that owns the method.
4. Arrow Functions
Arrow functions do not have their own this. They inherit this from the enclosing lexical scope.
5. Inside a Constructor
Inside a constructor, this refers to the newly created object.
6. Inside a Class
In a class method, this refers to the instance of the class.
7. Event Handlers
In event handlers, this typically refers to the element that fired the event.
8. this in setTimeout and setInterval
In a setTimeout or setInterval callback, this depends on the calling context.
9. this in a Module
In ES6 modules, this is undefined at the top level because modules are in strict mode by default. In CommonJS modules, this refers to module.exports.
call, apply and bind
In JavaScript, call, apply, and bind are methods available to functions that allow you to explicitly set the value of this and invoke or return the function with a specific context.
call
The call method invokes a function immediately and allows you to specify the value of this and pass arguments one by one.
apply
The apply method is similar to call, but it accepts arguments as an array or an array-like object.
bind
The bind method creates a new function with a specified this value and optional arguments, but does not invoke it immediately.
Prototype
In JavaScript, a prototype is a mechanism that allows objects to inherit properties and methods from other objects. It is fundamental to JavaScript's object-oriented programming model and enables features like inheritance and shared behaviors among objects.
How Prototypes Work
Every JavaScript object has an internal property called [[Prototype]] that links it to another object. This is accessible via the special property __proto__ or through Object.getPrototypeOf(). The object that is referenced by the [[Prototype]] serves as a prototype. If you try to access a property or method on an object and it doesn’t exist on that object, JavaScript looks for it in the object's prototype chain.
Function Prototypes
Functions in JavaScript have a special property called prototype. This is different from the __proto__ property. prototype is a property of functions that is used when creating objects via constructors. Objects created with a function constructor have their [[Prototype]] linked to the constructor's prototype.
Object Creation and Prototypes
You can directly set or create prototypes using Object.create().