What is the nullish coalescing operator in JavaScript?
Nullish coalescing is a JavaScript logical operator represented by two question marks (??). Nullish coalescing is an operator that returns the first “defined” value. “defined” here refers to an expression whose value is neither null nor undefined.
What is a spread operator?
Spread operator allows iterables such as arrays, objects, and strings to be expanded into single arguments. The spread operator is denoted by three dots (...) followed by the variable to be expanded.
Explain the “this” keyword.
To access the object, a method can use the this keyword.
In JavaScript, this keyword refers to the object it belongs to. Depending on the context, this might have several meanings. This pertains to the global object in a function and the owner object in a method, respectively.
What is Function Binding? What are the differences between call, apply, and bind?
Function binding:
Function binding refers to the process of associating a function with a specific context (this value). The bind() method creates a new function that, when called, has its this keyword set to the provided value.
const person = {
name: 'GFG',
greet: function() {
console.log('Hello, ' + this.name);
}
};
const greet = person.greet;
greet();
output: Hello, undefinedWhen greet is called directly (without being bound to the person object), the value of this is not person anymore. Instead, it refers to the global object (in non-strict mode) or is undefined (in strict mode).
call
The call() method immediately invokes a function, allowing you to set the value of this and pass arguments to the function.
const person = {
name: 'GFG',
greet: function(city) {
console.log('Hello, ' + this.name + ' from ' + city);
}
};
person.greet('Delhi');
const greet = person.greet;
greet.call(person, 'Noida');
output:
Hello, GFG from Delhi
Hello, GFG from NoidaThis code defines a greet method in the person object, calls it with ‘Delhi’ directly, and then uses call() to invoke the method with ‘Noida’, ensuring the this context is correctly bound to person.
apply
Similar to call(), the apply() method invokes a function and allows you to set the value of this, but the difference is that the arguments are passed as an array (or an array-like object).
const person = {
name: 'GFG',
greet: function(city, country) {
console.log('Hello, ' + this.name + ' from ' + city + ', ' + country);
}
};
person.greet('Delhi', 'India');
const greet = person.greet;
greet.apply(person, ['Noida', 'Delhi']);
output:
Hello, GFG from Delhi, India
Hello, GFG from Noida, DelhiThis code defines a greet() method in the person object, calls it with ‘Delhi’ and ‘India’, then uses apply() to invoke the method with ‘Noida’ and ‘Delhi’ by passing arguments as an array while maintaining the this context.
bind
The bind() method is used to create a new function that, when called, has its this value set to a specified value, regardless of how the function is invoked.
const person = {
name: 'GFG',
greet: function() {
console.log('Hello, ' + this.name);
}
};
const greet = person.greet;
const boundGreet = greet.bind(person);
boundGreet();
output: Hello, GFGThis code defines a person object with a greet method, binds it to the person object using bind(), and then calls the bound function to correctly reference the person’s name when executed.
What are anonymous functions in JavaScript?
An anonymous function is a function that does not have any name associated with it. We usually use the function keyword followed by the function’s name to define a function in JavaScript. Anonymous functions omit the function name, making it not accessible after its creation.
An anonymous function can only be accessed by a variable. The anonymous nature of the function makes it great for passing in functions as arguments to other functions (higher-order functions) and functions that are invoked immediately upon initialization.
What is hoisting in JavaScript?
Hoisting refers to the process where the interpreter moves the declaration of classes, functions, or variables to the top of their scope, before their execution.
Hoisting allows developers to reference variables, functions, and classes before they are declared.
Referencing a variable before its initialization would return the variable’s default value (undefined for variables declared using the var keyword).
console.log(foo); var foo = 'foo';
It might surprise you that this code outputs undefined and doesn’t fail or throw an error – even though foo gets assigned after we console.log it!
This is because the JavaScript interpreter splits the declaration and assignment of functions and variables: it “hoists” your declarations to the top of their containing scope before execution.
This process is called hoisting, and it allows us to use foo before its declaration in our example above.
What is a callback function in JavaScript?
A callback function is a function passed into another function as an argument. The callback function is then invoked inside the callee to complete an action.
What are the different states of a Promise?
Pending – Promise’s initial state, waiting for the operation to completeFulfilled – Promise’s operation was completed successfullyRejected – Promise’s operation failedSettled – Promise is either fulfilled or rejectedHow to handle errors using Promises?
Promise chains are great at error handling. When a promise rejects, the control jumps to the closest rejection handler. That’s very convenient in practice.
The .catch doesn’t have to be immediate. It may appear after one or maybe several .then.
Normally, such .catch doesn’t trigger at all. But if any of the promises above rejects (a network problem or invalid json or whatever), then it would catch it.
The code of a promise executor and promise handlers has an “invisible try..catch” around it. If an exception happens, it gets caught and treated as a rejection.
The “invisible try..catch” around the executor automatically catches the error and turns it into rejected promise.
This happens not only in the executor function, but in its handlers as well. If we throw inside a .then handler, that means a rejected promise, so the control jumps to the nearest error handler.
new Promise((resolve, reject) => {
resolve("ok");
}).then((result) => {
throw new Error("Whoops!"); // rejects the promise
}).catch(alert); // Error: Whoops!What is Promise.all?
Promise.all is a type of Promise that accepts an array of Promises and waits for each Promise to resolve. Promise.all resolves once each of the Promise inputs resolves, emitting an array of results in the then block. A rejected Promise in the input will cause the Promise.all Promise to also get rejected.
Explain async/await in JavaScript.
The async keyword is placed before a function to denote that the function is asynchronous and can be used as a Promise.
The await keyword, on the other hand, tells JavaScript to wait for the async operation to complete before proceeding to the next task in the function. The await keyword can only be used in an async function.
What are rest parameters?
The rest parameter syntax allows a function to accept an indefinite number of arguments as an array. The rest operator puts the contents of the variable after the rest operator into an array (rest parameter can only be used as the last parameter of a function).
Rest operator is represented by three dots (...) followed by the variable name. The variable can then be used to access the array containing the contents of the function’s arguments.
What is a WeakMap and WeakSet in JavaScript?
WeakMap:
The first difference between Map and WeakMap is that keys must be objects, not primitive values.
If we use an object as the key in it, and there are no other references to that object – it will be removed from memory (and from the map) automatically.
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // overwrite the reference
// john is removed from memory!let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // overwrite the reference
// john is stored inside the map,
// we can get it by using map.keys()Compare it with the regular Map example above. Now if john only exists as the key of WeakMap – it will be automatically deleted from the map (and memory).
WeakMap does not support iteration and methods keys(), values(), entries(), so there’s no way to get all keys or values from it.
WeakMap has only the following methods:
weakMap.set(key, value)weakMap.get(key)weakMap.delete(key)weakMap.has(key)
Why such a limitation? That’s for technical reasons. If an object has lost all other references (like john in the code above), then it is to be garbage-collected automatically. But technically it’s not exactly specified when the cleanup happens.
WeakSet:
WeakSet behaves similarly:
Set, but we may only add objects to WeakSet (not primitives).Set, it supports add, has and delete, but not size, keys() and no iterations.let visitedSet = new WeakSet();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
visitedSet.add(john); // John visited us
visitedSet.add(pete); // Then Pete
visitedSet.add(john); // John again
// visitedSet has 2 users now
// check if John visited?
alert(visitedSet.has(john)); // true
// check if Mary visited?
alert(visitedSet.has(mary)); // false
john = null;
// visitedSet will be cleaned automaticallyThe most notable limitation of WeakMap and WeakSet is the absence of iterations, and the inability to get all current content. That may appear inconvenient, but does not prevent WeakMap/WeakSet from doing their main job – be an “additional” storage of data for objects which are stored/managed at another place.
What is typecasting in JavaScript?
Typecasting or coercion means to change the data type of a value to another data type. For example, a conversion from a string to an integer or vice versa.
Coercion can either be implicit or explicit. Implicit coercion is when the type conversion is automatic, whereas explicit coercion is when a developer explicitly writes code to convert the type of a value. The latter is also known as typecasting.
There are three typecasts provided by JavaScript:
Boolean(value) – Casts the input value to a boolean valueNumber(value) – Casts the input value to a float or integer valueString(value) – Casts the input value to a stringWhat is event bubbling in JavaScript?
When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>A click on the inner <p> first runs onclick:
<p>.<div>.<form>.Event bubbling is a way of event propagation in the HTML DOM API, where events are handled from starting from the innermost element propagating outwards to its parent elements.
Let’s look at an example where an event occurs in a nested element where both elements have a registered event handler for the triggered event. Event bubbling causes the event to be captured and handled by the innermost element first. It is then propagated to the outer elements.
What is event capturing in JavaScript?
Event capturing is another way of event propagation in the HTML DOM API. Unlike event bubbling, event capturing propagates an event from the outermost element to the target element.
There’s another phase of event processing called “capturing”. It is rarely used in real code, but sometimes can be useful.
The standard DOM Events describes 3 phases of event propagation:
Capturing phase – the event goes down to the element.Target phase – the event reached the target element.Bubbling phase – the event bubbles up from the element.Capturing phase is rarely used.
In fact, the capturing phase was invisible for us, because handlers added using on<event>-property or using HTML attributes or using two-argument addEventListener(event, handler) don’t know anything about capturing, they only run on the 2nd and 3rd phases.
To catch an event on the capturing phase, we need to set the handler capture option to true:
elem.addEventListener(..., {capture: true})
// or, just "true" is an alias to {capture: true}
elem.addEventListener(..., true)Note that while formally there are 3 phases, the 2nd phase (“target phase”: the event reached the element) is not handled separately: handlers on both capturing and bubbling phases trigger at that phase.
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
<script>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`));
}
</script>The code sets click handlers on every element in the document to see which ones are working.
If you click on <p>, then the sequence is:
HTML → BODY → FORM → DIV -> P
P → DIV → FORM → BODY → HTML
P (capturing phase, the first listener):HTML → BODY → FORM → DIV -> P
P → DIV → FORM → BODY → HTML
P (bubbling phase, the second listener).
Please note, the HTML → BODY → FORM → DIV -> P
P → DIV → FORM → BODY → HTML
P shows up twice, because we’ve set two listeners: capturing and bubbling. The target triggers at the end of the first and at the beginning of the second phase.
What is a closure in JavaScript?
A closure is an inner function that has access to the variables in the enclosing/outer function’s scope. The closure has access to variables from three scopes:
Closures are particularly useful on the web because of the web’s event-based nature. A common pattern in front-end JavaScript is as follows: you define a behavior, then attach it to an event that is triggered by user input.
When a function is defined within another function, it retains access to the variables and parameters of the outer function, even after the outer function has finished executing. That link, between the inner function and its scope inside the outer function is known as closure
You can use them to create private variables that can only be accessed by the inner function, you can even use them to create complex objects with access to a rich context that is only available globally to them.
What does the instanceof operator do?
The instanceof operator checks whether the prototype property of a constructor appears anywhere in the prototype chain of an object. In other words, the instanceof operator checks if the object is an instance of a class or not at run time.
How do you create a shallow copy of an object?
Deep copying means that the value of the new variable is disconnected from the original variable while a shallow copy means that some values are still connected to the original variable.
First of all, a deep copy is a copy of an object completely disconnected from the original variable. A shallow copy on the other hand is a copy of the original variable where some values are still connected to the original variable.
There are two main ways to create a shallow copy of an object in JavaScript:
spread operatorObject.assign()What is the difference between local storage and session storage?
Local storage is a read-only property of the window interface to access a storage object. The stored data is saved indefinitely across browser sessions. Data stored in local storage is only cleared when removed explicitly through the browser’s settings or programmatically by the application.
Session storage is similar to local storage with the key difference being the data stored’s expiration time. Data stored in session storage gets cleared when the page session ends. A page session lasts as long as the tab or browser is open and persists between page reloads and restores.
What is the Temporal Dead Zone in JavaScript?
In ES6, variables declared using let and const are hoisted similar to var, class and function. However, there is a period between the variable’s declaration and when it enters scope where the variable can’t be accessed. This period is called the Temporal dead zone (TDZ).
When the interpreter hoists a variable declared with var, it initializes its value to undefined. The first line of code below will output undefined:
console.log(foo); // undefined var foo = 'bar'; console.log(foo); // "bar"
Variables declared with let and const are hoisted but not initialized with a default value. Accessing a let or const variable before it’s declared will result in a ReferenceError:
{
// Start of foo's TDZ
let bar = 'bar';
console.log(bar); // "bar"
console.log(foo); // ReferenceError because we're in the TDZ
let foo = 'foo'; // End of foo's TDZ
}What are the differences between mutable and immutable objects?
Mutable Objects:
In JavaScript, the following types are mutable: arrays, objects, and functions.
Using the push() and pop() methods are mutate the original array by adding and removing elements.
Immutability:
JavaScript primitives such as strings and numbers are immutable. Once created, their values cannot be changed.
Benefits:
Mutability can lead to unexpected side effects and bugs when multiple parts of your code share and modify the same object.Immutability helps prevent such issues by ensuring that once an object is created, it remains unchanged. This simplifies program flow and debugging.What is the difference between the var and let keywords in JavaScript?
The var and let keywords are both used to declare variables in JavaScript, but they have some key differences.
Scope
The main difference between var and let is the scope of the variables they create. Variables declared with var have function scope, which means they are accessible throughout the function in which they are declared. Variables declared with let have block scope, which means they are only accessible within the block where they are declared.
Hoisting
Another difference between var and let is that var declarations are hoisted, while let declarations are not. Hoisting means that the declaration of a variable is moved to the top of the scope in which it is declared, even though it is not actually executed until later.
Redeclaration
Finally, var declarations can be redeclared, while let declarations cannot. This means that you can declare a variable with the same name twice in the same scope with var, but you will get an error if you try to do the same with let.
What are object prototypes?
Object prototypes in JavaScript are a fundamental concept that enables inheritance and the sharing of properties and method objects.
The prototype object can have its own prototype. Forming a chain known as the prototype chain.