Table of Contents
What are Closures in JavaScript? Provide an example.
A closure is made up of the lexical context in which a function was declared and the function itself. It is an inner function that has access to the variables of the outer or enclosing function, for example. The closure has three chain scopes.
- Variables defined within the scope of the own code between its curly brackets
- variables of the outer function
- Global variables
Javascript
function outerFunction() { // Variable declared in the outerFunction scope let outerVariable = 'I am from the outer function!'; // Inner function (closure) declared inside outerFunction function innerFunction() { console.log(outerVariable); // innerFunction has access to outerVariable } // Return the inner function from the outer function return innerFunction; } // Call the outer function and store the returned inner function const closureFunction = outerFunction(); // Even though outerFunction has finished executing, closureFunction retains access to outerVariable closureFunction(); // Output: "I am from the outer function!"
Closures are incredibly useful in situations like callbacks, event handling, and creating private variables in JavaScript, as they provide a way to encapsulate and retain data within a function's scope even after the function has completed its execution.
How do Promises and Async/Await work in JavaScript? Provide an example using both.
Promises and Async/Await are features in JavaScript used to handle asynchronous operations in a more organized and readable manner, making it easier to work with asynchronous code.Promises:
Promises are objects that represent the eventual completion (or failure) of an asynchronous operation and allow us to handle the result once it's available. A promise can be in one of three states: pending (operation is still ongoing), fulfilled (operation completed successfully), or rejected (operation failed).
Here's an example using Promises to fetch data from an API:
Here's an example using Promises to fetch data from an API:
Javascript
// Function that returns a Promise to fetch data from an API function fetchDataFromAPI() { return new Promise((resolve, reject) => { // Simulating an API call with setTimeout setTimeout(() => { const data = { message: 'Data fetched successfully!' }; // Simulate success resolve(data); // Simulate failure // reject(new Error('Failed to fetch data!')); }, 2000); }); } // Using the Promise fetchDataFromAPI() .then((data) => { console.log(data.message); // Output: "Data fetched successfully!" }) .catch((error) => { console.error(error.message); // Output (if rejected): "Failed to fetch data!" });
Async/Await is a syntactical improvement introduced in ECMAScript 2017 (ES8) that makes working with Promises more concise and easier to read. The async keyword is used to define an asynchronous function, and within the function, the await keyword is used to wait for the resolution of a Promise.
Here's an example using Async/Await to fetch data from the same API:
Here's an example using Async/Await to fetch data from the same API:
Javascript
// Function that returns a Promise to fetch data from an API function fetchDataFromAPI() { return new Promise((resolve, reject) => { // Simulating an API call with setTimeout setTimeout(() => { const data = { message: 'Data fetched successfully!' }; // Simulate success resolve(data); // Simulate failure // reject(new Error('Failed to fetch data!')); }, 2000); }); } // Using Async/Await async function getData() { try { const data = await fetchDataFromAPI(); console.log(data.message); // Output: "Data fetched successfully!" } catch (error) { console.error(error.message); // Output (if rejected): "Failed to fetch data!" } } getData();
Both Promises and Async/Await are useful for managing asynchronous operations in JavaScript, and you can choose the approach that suits your coding style and project requirements.
Explain Prototypal Inheritance with an example
Prototypal Inheritance is a fundamental concept in JavaScript that allows objects to inherit properties and methods from other objects. In JavaScript, every object has a prototype, which is another object from which it inherits properties.Here's an example to illustrate Prototypal Inheritance:
Javascript
// Parent object constructor function function Animal(name, species) { this.name = name; this.species = species; } // Adding a method to the prototype of the Animal constructor Animal.prototype.makeSound = function () { return "Some generic sound"; }; // Creating instances of the Animal object const lion = new Animal("Leo", "Lion"); const dog = new Animal("Buddy", "Dog"); // Child object constructor function function Lion(name, species, maneColor) { // Call the parent constructor using call() to set the context Animal.call(this, name, species); this.maneColor = maneColor; } // Set the Lion prototype to be an instance of Animal, so Lion inherits from Animal Lion.prototype = Object.create(Animal.prototype); // Adding a method specific to the Lion constructor Lion.prototype.roar = function () { return "Roarrr!"; }; // Create an instance of the Lion object const myLion = new Lion("Simba", "Lion", "Golden"); // Using the inherited properties and methods console.log(myLion.name); // Output: "Simba" console.log(myLion.species); // Output: "Lion" console.log(myLion.maneColor); // Output: "Golden" console.log(myLion.makeSound()); // Output: "Some generic sound" console.log(myLion.roar()); // Output: "Roarrr!"
When we create an instance of Lion, it has access to its own properties (name, species, and maneColor) as well as the properties and methods inherited from Animal. This is the essence of Prototypal Inheritance in JavaScript, where objects can derive characteristics from other objects, forming a hierarchical relationship between them.
Explain Event Bubbling and Event Capturing with examples
Event Bubbling and Event Capturing are two different mechanisms in JavaScript that describe how events propagate through the DOM (Document Object Model) hierarchy.Event Bubbling:
Event bubbling is the default behavior in most browsers. When an event occurs on a particular element, that event first triggers on the innermost element and then propagates upward through its parent elements in the DOM hierarchy until it reaches the root of the document. This means the event is processed by the innermost element first and then "bubbles up" through its ancestors.
Example of Event Bubbling: Consider the following HTML structure:
JavaScript code:
When you click the "Click Me" button, the event will be triggered on the button first, then on the inner div, and finally on the outer div. The event bubbles up through the DOM tree. You'll see the following output in the console:
Event Capturing (Capture Phase):
Event capturing is the opposite of event bubbling. In this phase, the event starts from the root of the document and travels down the DOM tree to the target element. This happens before the actual event is triggered on the target element.
Example of Event Capturing: Let's modify the previous example to use event capturing:
Example of Event Bubbling: Consider the following HTML structure:
HTML
<div id="outer"> <div id="inner"> <button id="btn">Click Me</button> </div> </div>
Javascript
const btn = document.getElementById("btn"); btn.addEventListener("click", (event) => { console.log("Button clicked!"); console.log("Target: ", event.target); console.log("Current Target: ", event.currentTarget); });
Terminal
Button clicked! Target: <button id="btn">Click Me</button> Current Target: <button id="btn">Click Me</button> Button clicked! Target: <div id="inner">...</div> Current Target: <div id="inner">...</div> Button clicked! Target: <div id="outer">...</div> Current Target: <div id="outer">...</div>
Event capturing is the opposite of event bubbling. In this phase, the event starts from the root of the document and travels down the DOM tree to the target element. This happens before the actual event is triggered on the target element.
Example of Event Capturing: Let's modify the previous example to use event capturing:
Javascript
const btn = document.getElementById("btn");
btn.addEventListener(
"click",
(event) => {
console.log("Button clicked!");
console.log("Target: ", event.target);
console.log("Current Target: ", event.currentTarget);
},
true // Add 'true' as the third parameter to enable event capturing
);
Terminal
Button clicked! Target: <div id="outer">...</div> Current Target: <button id="btn">Click Me</button> Button clicked! Target: <div id="inner">...</div> Current Target: <button id="btn">Click Me</button> Button clicked! Target: <button id="btn">Click Me</button> Current Target: <button id="btn">Click Me</button>
Event Bubbling | Event Capturing (Capture Phase) | |
---|---|---|
Order | Event starts from target and propagates upward to the root | Event starts from the root and propagates downward to the target |
Phases | Bubbles in the bubbling phase | Captures in the capture phase |
Execution Order | Outermost element first, innermost element last | Outermost element first, innermost element last |
Event Listeners | Add event listeners with addEventListener(eventType, handler) without specifying the third parameter (default is false ) | Add event listeners with addEventListener(eventType, handler, true) to enable capturing |
Browser Support | Supported in almost all modern browsers | Supported in almost all modern browsers |
Common Use Case | Commonly used due to default behavior. Often used for event delegation | Less commonly used, but useful in specific cases when capturing is needed |
What is the difference between Call, Apply and Bind?
`call`, `apply`, and `bind` are methods used to set the context (the value of this) of a function explicitly. They allow you to control how a function is called and provide a way to borrow functions, set the `this` value, and pass arguments.call: The `call` method is used to call a function with a specified `this` value and individual arguments passed as arguments. It allows you to invoke a function immediately and explicitly specify the context in which the function should be executed.
Syntax of call:
Javascript
function.call(thisArg, arg1, arg2, ...);
Javascript
const person1 = { name: 'John', greet: function (greeting) { return `${greeting}, ${this.name}!`; }, }; const person2 = { name: 'Jane', }; const greeting = person1.greet.call(person2, 'Hello'); console.log(greeting); // Output: "Hello, Jane!"
apply: The apply method is similar to call, but instead of passing individual arguments, it accepts an array-like object as the second argument. This can be useful when you have a function with a variable number of arguments.
Syntax of apply:
Javascript
function.apply(thisArg, [arg1, arg2, ...]);
Javascript
function sum(a, b, c) { return a + b + c; } const numbers = [1, 2, 3]; const result = sum.apply(null, numbers); console.log(result); // Output: 6
bind: The `bind` method returns a new function that has the same body as the original function but with a fixed `this` value. Unlike `call` and `apply`, `bind` doesn't immediately invoke the function; instead, it creates a new function with a preset context that can be called later.
Syntax of bind:
Javascript
function.bind(thisArg, [arg1, arg2, ...]);
Example of bind:
Javascript
const dog = { name: 'Buddy', sound: 'Woof!', bark: function () { console.log(this.sound); }, }; const button = document.getElementById('myButton'); // Bind the bark function to the dog object const barkFunction = dog.bark.bind(dog); // Attach the bound function to the button's click event button.addEventListener('click', barkFunction);
call | apply | bind | |
---|---|---|---|
Immediate Execution | Yes | Yes | No |
Passing Arguments | Individual arguments | Array-like object | Individual arguments (currying) |
Returns | Function execution | Function execution | New bound function |
Usage | When you know the number of arguments | When you have an array-like object | When you need to create a reusable bound function |
0 Comments