Most commonly asked JavaScript interview questions

Most commonly asked JavaScript interview questions

What is Hoisting?

In javascript, Hosting is referred to the process where the interpreter allocated memory for variables and functions even before declaring it.

Example of Variable lifecycle:

let a;               // Declaration
a = 100;            // Assignment
console.log(a);    // Usage

Example of Hoisting:

console.log(a);  // undefined 
console.log(fun); // [Function: fun]

var a = 10; // Declaration
function fun() {
    console.log('fun');
}


console.log(a);  // reference error 
console.log(fun); // [Function: fun]


const a = 10;
function fun() {
    console.log('fun');
}

What is Scope?

Scope in JavaScript refers to the current context of code, which determines the accessibility of variables to JavaScript.

// global scope
let var1 = 10;

function fun() {
    // function scope / local scope
    let var1 = "var1"; 
    console.log(var1);
}
fun(); // var1
console.log(var1); // 10

What are Closures?

In JavaScript, a closure is created whenever a function is defined inside another function. A closure allows the inner function to access the outer function's variables and parameters, even after the outer function has returned. This is possible because the inner function has access to the scope chain of the outer function.

Here is an example of a closure in JavaScript:

function outerFunction() {
  const outerVariable = "I am outside!";

  function innerFunction() {
    console.log(outerVariable); // logs "I am outside!"
  }

  return innerFunction;
}

const innerFunc = outerFunction();
innerFunc(); // logs "I am outside!"

In this example, the inner function innerFunction() is defined inside the outer function outerFunction(). The outerFunction() creates a variable outerVariable and defines innerFunction(), which logs the outerVariable. The outerFunction() then returns the innerFunction().

When outerFunction() is called and the returned innerFunction() is executed, it has access to the outerVariable even though it is technically outside of its scope. This is because the innerFunction() forms a closure around the outerVariable, allowing it to access it even after outerFunction() has returned.

Closures are often used in JavaScript to create private variables and methods, as well as to create functions that "remember" previous values of their arguments or variables.

What is the event.currentTarget?

event.currentTarget is a property of an Event object that refers to the element on which the event is currently being handled. This property is useful when you have numerous elements with event listeners and we want to know which element triggered the event.

Consider the following HTML code:

<div id="parent">
  <button id="child">Click me!</button>
</div>

Now let's add a click event listener to both the parent and the child elements:

const parent = document.querySelector('#parent');
const child = document.querySelector('#child');

parent.addEventListener('click', handleClick);
child.addEventListener('click', handleClick);

function handleClick(event) {
  console.log(event.currentTarget.id);
}

When we click the button, the handleClick function is called and passed an event object as an argument. The event.currentTarget property refers to the element to which the event listener was attached, regardless of which element triggered the event. In this case, clicking the child button triggers both the parent and child event listeners, but the event.currentTarget.id will always log "parent" since the event listener was attached to the parent element.

If you want to access the element that actually triggered the event, you can use the event.target property instead of event.currentTarget. The event.target property refers to the actual element that triggered the event, which in this case would be the child button.

What's the difference between \== and ===?

Both == and === are used as a compare operator for comparing two values in a program. The main difference between the two is the type of comparison they perform.

The == the operator performs an abstract equality comparison. It compares two values for equality after converting both values to a common type. If the types of the two values are different, the values are coerced to a common type before comparison. For example, the string "5" would be equal to the number 5 if compared.

Here are some examples of using the == operator:

1 == "1"    // true
null == undefined   // true
true == 1   // true

On the other hand, the === an operator performs a strict equality comparison. It compares two values for equality without performing any type of conversion. If the types of the two values are different, the values are considered unequal.

Here are some examples of using the === operator:

1 === "1"    // false
null === undefined   // false
true === 1   // false

In general, it is a common practice to use the === operator as it avoids type coercion and can help avoid unexpected results or errors in your code. However, there may be cases where you want to use the == operator for loose equality comparison.

How to evaluate multiple expressions in one line?

In the case of JavaScript, we can evaluate numerous expressions in one line by separating them with the comma operator (,). The comma operator allows you to include multiple expressions in a single statement and evaluates them from left to right. The value of the last expression is returned as the result of the entire expression.

Here's an example of using the comma operator to evaluate numerous expressions in one line:

let x = 1, y = 2, z = 3;
x++, y++, z++;  // evaluates to 2, 3, 4
console.log(x, y, z);  // outputs "2 3 4"

In this example, the expressions x++, y++, and z++ are separated by commas, allowing them to be evaluated in a single line. Each expression increments its corresponding variable by 1, and the final values of x, y, and z are logged to the console.

You can also use the comma operator in other ways, such as to initialize multiple variables in a single statement:

let a = 1, b = 2, c = 3;

In this example, the comma operator is used to separate the initialization of three variables (a, b, and c) in a single statement.

Note that while using the comma operator can make your code more straightforward, it can also make it less readable and more difficult to debug.

What are the falsy values in JavaScript?

A value is considered falsy in Javascript. if it is treated as false when evaluated in a boolean context. Here is a list of the falsy values in JavaScript:

  • false: the boolean value false

  • 0: the number zero

  • null: a null value

  • undefined: an undefined value

  • NaN: Not-a-Number

In addition, the empty string ("") is also considered falsy.

Here's an example that shows how falsy values work in JavaScript:

if (0 || null || undefined || NaN || "") {
  // This code will not execute because all values are falsy
} else {
  // This code will execute because all values are falsy
}

In the above example, the if statement checks if any of the values inside the parentheses is truthy. However, all of the values (0, null, undefined, NaN, and "") are falsy values, so the code inside the if block will not execute. Instead, the code inside the else block will execute because all of the values are falsy.

What does "use strict" do?

"use strict" is a directive that enables strict mode, a feature introduced in ECMAScript 5 (ES5) that allows us to place our code in a stricter operating context.

When strict mode is enabled, the JavaScript engine implements stricter rules for code syntax and behavior, which can help catch coding mistakes and make your code more secure.

Here are some of the effects of enabling strict mode with "use strict":

  1. Prevents the use of undeclared variables: In strict mode, any attempt to use a variable that has not been declared with var, let, or const will throw a ReferenceError. This helps prevent the accidental creation of global variables, which can cause unexpected behavior.

  2. Prohibits duplicate property names: In strict mode, any attempt to define multiple properties with the same name in an object literal or class declaration will throw a SyntaxError.

  3. Disables the use of the with statement: In strict mode, the with statement is not allowed. This is because it can make code harder to read and understand, and can also cause security vulnerabilities.

  4. Prevents assignment to non-writable properties: In strict mode, any attempt to assign a value to a non-writable property or a getter-only property will throw a TypeError.

  5. Disallows the deletion of variables and functions: In strict mode, the delete operator cannot be used to delete variables, functions, or function arguments.

  6. Makes eval() it is safer: In strict mode, the eval() function is executed in its own scope, rather than in the current scope. This helps prevent unexpected variable declarations or changes to existing variables.

To enable strict mode in a JavaScript file, you can simply add the "use strict" directive at the top of the file or within a function.

For example:

"use strict";

function myFunction() {
  // function code here
}

By enabling strict mode, you can write safer, more reliable JavaScript code.

What's the value of "this" in JavaScript?

The value of the this keyword is determined by how a function is called. It refers to the object that the function is a method of or the global object if the function is called a standalone function. The value this is often referred to as the "context" of the function.

Here are some common rules that determine the value of this in different contexts:

  1. Global context: When a function is called in the global context, i.e. not as a method of an object, this refers to the global object in Node.js.
console.log(this);  // logs the global object
  1. Object method context: When a function is called as a method of an object, this refers to the object that the method is called on.
const myObject = {
  name: "John",
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

myObject.greet();  // logs "Hello, my name is John"

In this example, greet() is called a method of myObject, so this refers to myObject.

  1. Constructor context: When a function is called with the new keyword, this refers to the newly created object.
function Person(name) {
  this.name = name;
}

const john = new Person("John");
console.log(john.name);  // logs "John"

In this example, Person() is called with the new keyword, so this refers to the newly created Person object.

  1. Explicit context: You can also set the value of this explicitly using methods like call() or apply(). In this case, this refers to the object passed as the first argument to the method.
function greet() {
  console.log(`Hello, my name is ${this.name}`);
}

const person = { name: "John" };
greet.call(person);  // logs "Hello, my name is John"

In this example, call() is used to call greet() with person as the value of this.

The value of this can be a little tricky to understand in some cases, so it's important to be familiar with the rules that determine its value in different contexts.

What is an Event loop?

The event loop is a mechanism that allows Javascript to handle asynchronous tasks, such as I/O operations(API calls or DB calls) or timers (setTimeout), in a non-blocking way.

The event loop is part of the JavaScript runtime environment and is responsible for managing the execution of tasks in a single-threaded environment.

When an asynchronous task is initiated, such as a timer or an API request, it is added to a queue. The event loop constantly checks this queue for tasks to execute. When a task is ready to be executed, the event loop takes it out of the queue and runs it. If the task is a long-running operation, such as a complex computation, the event loop will continue checking the queue for other tasks to execute while the operation runs.

Here is a simplified example of how the event loop works:

  1. The JavaScript runtime environment is initialized, and the event loop starts running.

  2. An asynchronous task, such as a timer or an API request, is initiated and added to the task queue.

  3. The event loop checks the task queue for tasks to execute. If there is a task in the queue, the event loop takes it out and runs it.

  4. If the task is a long-running operation, such as a complex computation, the event loop continues checking the task queue for other tasks to execute while the operation runs.

  5. When the task is completed, its callback function is added to the task queue.

  6. The event loop checks the task queue for callback functions to execute. If there is a callback function in the queue, the event loop takes it out and runs it.

  7. If there are no more tasks or callback functions in the queue, the event loop waits for new tasks to be added to the queue.

The event loop allows JavaScript to handle asynchronous tasks without blocking the main thread, which can improve the performance and responsiveness of web applications. Nevertheless, it also introduces some complexity to the language, as developers need to understand how the event loop works in an order to write code.

Link to Javascript Eventloop Visualizer: https://www.jsv9000.app/

What is the prototype of an object?

A prototype is a template or blueprint for an object that defines its properties and behaviors. It serves as the basis for creating new instances of the object.

In JavaScript, prototypes are used to implement inheritance. When an object is created, it inherits properties and behaviors from its prototype. If a property or behavior is not defined in the object itself, the runtime system looks for it in the prototype chain.

For example, consider a prototype for a car object. The car prototype might define properties such as the make, model, and color of the car, as well as methods such as drive and stop. When a new car object is created from the prototype, it inherits these properties and methods, but can also have its own unique properties and methods.

In summary, a prototype is a template or blueprint for an object that defines its properties and behaviors and serves as the basis for creating new instances of the object.

What is an IIFE, and what is its use of it?

IIFE or Immediately Invoked Function Expression is a design pattern in JavaScript that allows a function to be executed immediately after it is defined.

An IIFE is defined as an anonymous function expression that is wrapped in parentheses and is immediately followed by another set of parentheses. The inner set of parentheses is used to invoke the function instantly after it is defined.

Here's an example of an IIFE:

(function () {
  // code to be executed immediately
})();

The code inside the function is executed instantly after the function is defined. The outer parentheses around the function expression are necessary to indicate that it is a function expression and not a function declaration.

The main use of an IIFE is to create a private scope for the code inside the function. This means that any variables or functions defined inside the IIFE are not accessible from outside the IIFE, and do not pollute the global namespace. This can help avoid naming collisions and improve the overall organization and maintainability of the code.

IIFEs are commonly used in JavaScript libraries and frameworks to define modules and encapsulate functionality. They can also be used to create closures and pass arguments to a function without exposing them to the global scope.

What are Higher Order Functions?

In JavaScript, higher-order functions are functions that can take one or more functions as arguments, and/or return a function as their result. This means that higher-order functions can operate on other functions, either by taking them as arguments or by returning them as results.

Here's an example of a higher-order function that takes a function as an argument:

function apply(func, arg) {
  return func(arg);
}

In this example, the apply function takes two arguments: a function func, and an argument arg. The apply function then applies the func function to the arg argument and returns the result. This allows us to apply different functions to different arguments, without having to write separate functions for each use case.

Here's an example of a higher-order function that returns a function as its result:

function multiplyBy(num) {
  return function(x) {
    return x * num;
  }
}

In this example, the multiplyBy the function takes a number num as its argument, and returns another function that takes a number x as its argument, and multiplies it by num. This allows us to create different functions that multiply a number by a specific factor, without having to repeat the same code multiple times.

Higher-order functions are commonly used in functional programming, where they can be used to create powerful abstractions and compose complex behavior from simpler building blocks. They are also commonly used in JavaScript libraries and frameworks, where they can be used to create reusable and extensible code.

What are Promises & how it's working?

In JavaScript, Promises are a way to handle asynchronous operations, such as network requests or file I/O, in a more readable and manageable way. Promises are objects that represent the eventual completion (or failure) of an asynchronous operation and allow you to attach callbacks to be executed when the operation succeeds or fails.

A Promise has three states:

  • pending: The initial state, before the operation has been completed.

  • fulfilled: The state reached when the operation has been completed successfully, and the result (or value) of the operation is available.

  • rejected: The state reached when the operation has failed, and the reason for the failure (or error) is available.

A Promise can be created using the Promise constructor, which takes a single argument: a function (often called the executor function) that takes two arguments: resolve and reject. The resolve function is used to fulfill the Promise with a value, and the reject function is used to reject the Promise with a reason (usually an error).

Here's an example of creating and using a Promise:

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber < 0.5) {
      resolve(randomNumber);
    } else {
      reject(new Error('Number is greater than 0.5'));
    }
  }, 1000);
});

myPromise.then((result) => {
  console.log('Promise fulfilled with result:', result);
}).catch((error) => {
  console.error('Promise rejected with error:', error);
});

In this example, we create a Promise that simulates an asynchronous operation by waiting for 1 second and then resolving or rejecting with a random number. We attach a then callback to be executed when the Promise is fulfilled, and a catch callback to be executed when the Promise is rejected.

When the Promise is fulfilled, the then callback is executed with the result (the random number) as its argument. When the Promise is rejected, the catch callback is executed with the reason (the error) as its argument.

Promises allow you to write asynchronous code in a more readable and manageable way, by providing a standardized way to handle asynchronous operations and their results. They also allow you to chain multiple asynchronous operations together and handle errors in a consistent way.

Did you find this article valuable?

Support Vinayak Sharma by becoming a sponsor. Any amount is appreciated!