JavaScript Interview Questions

1. What are logical operators in JavaScript?

Logical operators allow developers to compare variables and perform tasks based on the result of the comparison. As a hiring manager, you’d ask this question to gauge the candidate’s familiarity with the language and its fundamental features. Your candidate should be able to explain each logical operator and their behavior – stepping through each operand and computing its output.

There are four logical operators in JavaScript:

  • || – OR
  • && – AND
  • ! – NOT
  • ?? – Nullish Coalescing (see next question)

OR

The “OR” operator is represented by two vertical lines (||). In JavaScript, the “OR” operator evaluates the values from left to right and returns the first truthy value. If none of the values are truthy, the “OR” operator will return the last operand.

let x = 'Hello' || false; // x is equal to 'Hello' (first truthy value)

let y = false || 'Yes' || 1; // y is equal to 'Yes' (first truthy value)

let z = false || undefined || 0; // since all are false, z is equal to 0 (the last value)

AND

The “AND” operator is represented by two ampersands (&&). In JavaScript, the “AND” operator evaluates the values from left to right and returns the first falsy value. If all the operands are true, the “AND” operator will return the last operand.

let x = 'Hello' && false; // x is equal to 'false' (first falsy value)

let y = 0 && 'Yes' && true; // y is equal to 0 (first falsy value)

let z = true && 'Hello' && 10; // since all are truthy, z is equal to 10 (the last value)

NOT

The “NOT” operator is represented by an exclamation mark (!). the “NOT” operator accepts a single argument and returns the inverse of its boolean value. The argument is first converted into a boolean (true or false). The argument’s boolean value is then inverted and returned (true becomes false and vice versa).

let x = !false; // x is equal to true

let y = !('Hello'); // y is equal to false ('Hello' is truthy)

2. What is the nullish coalescing operator in JavaScript?

Nullish coalescing is an addition to JavaScript that helps provide a nicer and more concise syntax for getting the first “defined” value. The candidate should be able to explain what nullish coalescing is at a high-level and how to use the operator when asked this JS interview question.

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.

Let’s look at how the operator works.

a ?? b

The output of the code above is as follows:

  • if a is defined, the value of a is returned
  • if a isn’t defined, the value of b is returned

Let’s look at a few examples of this operator when given a different set of arguments.

let undefinedUser;
console.log(undefinedUser ?? 'Anonymous'); // will print 'Anonymous'

let definedUser = 'Ryan';
console.log(definedUser ?? 'Anonymouse') // will print 'Ryan'

3. What is the difference between == and === operators?

JavaScript has two ways to test equality. Understanding the subtle difference between the two methods is important to prevent misusing each method. The candidate should be able to explain the differences and demonstrate a basic understanding of each method’s usage.

Both double-equals (==) and triple-equals (===) are comparison operators meant to compare the equality of two values.

Double-equals only compares the value of the element. Double-equals does type coercion, where the type of the variables is converted to a common type before checking their values. In short, the double-equals operator will return true for any two variables with the same value regardless of their type.

Triple-equals on the other hand, check for strict equality – both the value and type of the element being compared. Both the value and type of being compared has to match to satisfy the triple-equal operator

let x = 1; // number 1
let y = '1'; // string 1

if (x == y) {
    // true! Both x and y's values are equal
}

if (x === y) {
    // false! Although both x and y have the same value, x is a number whereas y is a string
}

4. What is a spread operator?

The spread operator is a feature from ES6 to help unpack an element. Candidates being asked this interview question on JavaScript should be able to demonstrate an understanding of how the spread operator expands an element – being able to come up with the output of 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.

Let’s look at an example where we combine two arrays using the spread operator. Below we have a male and female array containing a few strings each. The combined array combines the expanded male and female array, creating a single array with the contents from both male and female.

const male = ['Mike', 'Alex', 'Bob'];
const female = ['Sam', 'Maggie'];

const combined = [...male, ...female];

console.log(combined); // will print ['Mike', 'Alex', 'Bob', 'Sam', 'Maggie']

5. Explain loops in JavaScript.

We often require repeat actions. Loops are a way to execute the same code multiple times. The candidate should be able to explain how to use loops in JavaScript. An ideal answer should include the pros and cons of each looping method and its respective applications.

There are two main ways to create loops in JavaScript – while and for. Both methods consist of a condition to stop the loop and a “loop body”, the code that will be executed multiple times.

while loop

while loops are typically used when the “loop body” needs to be repeated an unknown number of times until the condition is met.

The code snippet below shows a simple while loop that prints the value of i on every iteration and stops when i is equal to 3.

let i = 0;
while (i < 3) {
  console.log(i); // will print 0, 1, and 2
  i++;
}

for loop

for loop, on the other hand, is better suited for executing the “loop body” a fixed number of times.

The same loop in the previous code snippet can be re-written using a for loop this way:

for (let i = 0; i < 3; i++) {
    console.log(i); // will print 0, 1, and 2
}

6. Explain the “this” keyword.

This keyword is widely used in JavaScript applications. It behaves differently compared to other languages such as Java and Python. The candidate should have a thorough understanding of how this keyword works and how it relates to its context.

The this keyword behaves differently depending on the caller’s context. Let’s look at a few contexts and what the this keyword refers to in each one

Global context

Global context refers to anything outside of any function – global object. this refers to the window object in web browsers and global object in Node.js applications.

If you assign a property to the this object in a web browser, JavaScript will add that property to the window object.

// in web browsers

this.name = 'Adam';

console.log(window.name) // will print 'Adam' in your console 

Function context

Functions can be invoked in four different ways.

  • Function invocation
  • Method invocation
  • Constructor invocation
  • Indirect invocation

Each of the invocations results in a different this behavior.

Function invocation

Depending on whether you are using “strict mode” or not, the this keyword refers to different values.

By default, the this keyword refers to the window or global object depending on where you are running the application.

// in web browsers

function callMe() {
    if (this === window) {
        // true!
    }
}

In “strict mode”, JavaScript sets the this keyword to undefined.

"use strict"

function callMe() {
    if (this === window) {
        // false!
    }
    if (this === undefined) {
        // true!
    }
}

Method invocation

When you call a method of an object (getName in the example below), the this keyword is set to the object that owns the method (user in the example below).

let user = {
    name: 'Bob',
    getName: function() {
        return this.name;
    }
}

console.log(user.getName()); // will print 'Bob' in your console

Constructor invocation

Constructor invocation is when the new keyword is used to create a new instance of a function object.

The new User('Bob') is a constructor invocation of the User function where a new instance of the User function is created. The this keyword in this case refers to the newly created object.

function User(name) {
    this.name = name;
}

User.prototype.getName = function() {
    return this.name;
}

let user = new User('Bob');
console.log(user.getName()); // will print 'Bob' in your console

Indirect invocation

Indirect invocation is when the callee of the function uses the call or apply keyword to call a function. Both these methods allow passing in the this value (bob and adam in the example below) as a parameter.

function sayHello(greeting) {
    console.log(`${gretting} ${this.name}`);
}

let bob = {
    name: 'Bob'
};
let adam = {
    name: 'Adam'
};

sayHello.call(bob, "Hello"); // will print 'Hello Bob' in your console
sayHello.call(adam, "Hi"); // will print 'Hi Adam in your console

The apply keyword is identical to the call keyword above. Instead of accepting a single value as the second parameter, the apply keyword expects an array of values.

sayHello.call(bob, ["Hello"]); // will print 'Hello Bob' in your console
sayHello.call(adam, ["Hi"]); // will print 'Hi Adam in your console

7. What are the differences between call, apply, and bind?

JavaScript has multiple ways to indirectly invoke a function. Your candidate needs to understand the differences between each and their use cases. You, as the candidate, should be able to explain not only their differences conceptually but also their use case and the reason behind it.

callapply, and bind are different methods to tie a function to an object and call the function within the specified context.

call

The call method invokes the function with the specified context – the function is called as if it’s part of the object.

The function sayHello in the example below references this.name which is part of the user object (out of the scope of the sayHello function). We can use the call function and pass in the user object as the first argument to tie the sayHello function and the user object momentarily, giving it access to the this.name property.

let user = { name: 'Bill' };

function sayHello(greeting){
  console.log(`${greeting} ${this.name}`)
}

sayHello('Hello'); // will print 'Hello'

sayHello.call(user, 'Hello'); // will print 'Hello Bill'

apply

The apply method is identical to the call method with the difference being in how each method accepts their arguments. The call method accepts an argument list, whereas the apply method accepts an array of arguments.

Using the same example as above, we can convert the call method to apply by wrapping the function’s arguments (excluding the context – user) in an array before passing it to apply method.

let user = { name: 'Bill' };

function sayHello(greeting){
  console.log(`${greeting} ${this.name}`)
}

sayHello.apply(user, ['Hello']); // will print 'Hello Bill'

bind

Unlike call and apply, the bind method doesn’t execute the function immediately. Instead, it returns a function that is tied to the object that can be executed later.

Let’s update the example again to use the bind method. We’ll first bind the sayHello function to the user object and assign it to a new variable (helloBill). We can then invoke that function calling it as you would a regular function.

let user = { name: 'Bill' };

function sayHello(greeting){
  console.log(`${greeting} ${this.name}`)
}

let helloBill = sayHello.bind(user);
helloBill('Hello'); // will print 'Hello Bill'

8. What are anonymous functions in JavaScript?

Anonymous functions serve numerous purposes in JavaScript. You might ask standard JavaScript interview questions like this one to gauge your candidates’ knowledge of functions in JavaScript and the various ways a function can be created and used. The candidate should be able to explain the difference between anonymous functions and other types of functions and what they are commonly used for

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.

The following snippet is an example of an anonymous function that is assigned to a variable (sayHello). The function can then be called by calling the variable name (sayHello) with the required arguments.

let sayHello = function (name) {
    console.log(`Hello ${name}`);
};
  
sayHello('Chris'); // will print 'Hello Chris'

9. What is hoisting in JavaScript?

Hoisting allows functions to be used safely before they are declared. This question will test the candidate’s familiarity with the JavaScript language and how classes, functions, and variables are interpreted by JavaScript. A basic understanding of hoisting can prevent unexpected errors caused by an incorrect order of declaration, initialization, and reference of a property. You may get other JavaScript hoisting interview questions, so study up!

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. Without hoisting, the order of the example below would need to be reversed, with the function declaration first, followed by the caller.

sayHello("Sam");

function sayHello(name) {
  console.log(`Hello ${name}`);
}

However, JavaScript only hoists its declarations, not their initializations. Referencing a variable before its initialization would return the variable’s default value (undefined for variables declared using the var keyword).

console.log(name); // will print 'undefined' from hoisted var declaration below
var name; // declaration
name = 'Mike'; // initialization
console.log(name); // will print 'Mike' after the previous line (initialization) is executed

10. What is a callback function in JavaScript?

JavaScript runs code sequentially from the top-down. However, sometimes, we need code to run after something has happened (i.e. asynchronous operations). Callback functions are a way to make sure a function runs only after the set of operations is completed. A candidate should be able to explain both how callback functions work and how it relates to asynchronous programming.

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.

The example below shows how the callback function is passed into and executed by another function. The last line (greetPerson(sayHello)) passes the sayHello function to the greetPerson function. greetPerson then executes the sayHello function by calling the callback variable, passing in the name value returned by the prompt function.

function sayHello(name) {
  console.log('Hello ' + name);
}

function greetPerson(callback) {
  let name = prompt('Name'); // displays a prompt to the user to submit a name
  callback(name);
}

greetPerson(sayHello);

11. What are Promises in JavaScript?

Promises are an effective way to handle asynchronous operations in JavaScript. A candidate should be able to demonstrate a high-level understanding of Promises and how they handle asynchronous operations. An ideal answer would include the tradeoffs of using Promises and how they compare to callbacks and events.

A Promise is a proxy for a value not necessarily known when the promise is created. A promise is a way to handle asynchronous operations in JavaScript. You can think of Promises as an alternative to callbacks and events.

Promises are ideal for handling multiple asynchronous operations, providing a better flow of control definition and error handling.

Let’s look at an example of a Promise that waits for a setTimeout to complete:

let timeoutPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise completed!'); // resolves the promise after 1 second
  }, 1000);
});

timeoutPromise.then((result) => {
  console.log(result); // will print 'Promise completed!' after 1 second (when the Promise completes)
});

12. What are the different states of a Promise?

Understanding the different states of a promise is important when dealing with promises to avoid unwanted side effects. You might ask this question to gain insight into the candidate’s familiarity with promises beyond the high-level concept.

Because of the asynchronous nature of Promises, a Promise has four states:

  • Pending – Promise’s initial state, waiting for the operation to complete
  • Fulfilled – Promise’s operation was completed successfully
  • Rejected – Promise’s operation failed
  • Settled – Promise is either fulfilled or rejected

13. What is Promise chaining?

Promise chaining is a common requirement when working with multiple Promises that depend on each other. A candidate should ideally be able to explain both what promise chaining is and how it is done in JavaScript.

One of the benefits of Promises is its chaining ability. A Promise can be chained using the then and catch functions. The then function will be called when the Promise completes successfully (fulfilled) whereas the catch function will be called when the Promise failed (rejected).

Each then and catch block can contain more Promises and be further chained, providing you with granular control over how each asynchronous operation should be executed.

Let’s look at an example of chaining two Promises that waits for one second between each execution.

new Promise((resolve) => {
  setTimeout(() => resolve(1), 1000);
}).then((result) => {
  console.log(result); // will print '1' after 1 second
  return new Promise((resolve) => {
      setTimeout(() => {
        resolve(2) // modify the value being resolved
      }, 1000)
  })
}).then((result) => {
  console.log(result); // will print '2' after another 1 second
  return result;
})

The first Promise in the code snippet above waits for one second before returning a result of 1. The code then goes to the then block where it executes the second Promise, waiting for another second before returning a result of 2.

14. What is Promise.all?

JavaScript interview questions like this one might be asked as a follow-up to the Promise chaining question. JavaScript provides several utility functions that help with chaining Promises – Promise.all being one of them. A candidate should be able to describe the function of this type of Promise and also how it alters the flow of the asynchronous functions.

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.

The example below shows how the Promise.all function is used to execute two Promises – Promise1 and Promise2, with a then block to capture the results of each Promise and a catch block to handle any errors.

Promise.all([Promise1, Promise2]).then(
    ([result1, result2]) => {
        // result1 contains the result of Promise1
        // result2 contains the result of Promise2
    }).catch((error) => {
        // when either Promise1 or Promise2 gets rejected
    });

15. Explain async/await in JavaScript.

Async and await are special syntax to work with Promises. In addition to the “what”, as an interviewer, you might also want to look for practical examples of async and await usages and how it differs from the Promise then syntax.

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.

Line 6 in the code snippet below pauses the function execution as it waits for the promise to resolve. Once the promise resolves, it will continue the execution, assigning the result of the promise to the result variable.

async function f() {
  let promise = new Promise((resolve) => {
    setTimeout(() => resolve('Promise resolved!'), 1000)
  });

  let result = await promise; // waits for 1 second, until the promise resolves
  console.log(result); // will print 'Promise resolved!'
}

Leave a Reply

Your email address will not be published. Required fields are marked *