OdinSchool OdinSchool
JavaScript Interview Questions - Must-Know These Tricky Questions

JavaScript Interview Questions - Must-Know These Tricky Questions

Summary

Our blog offers a deep dive into JavaScript interview preparation, beyond just tricky questions. It provides unique tips on engaging interviewers as well. Ideal for both newcomers and seasoned developers, this guide helps you navigate and conquer JavaScript interview challenges.

JavaScript interviews aren't just about knowing the basics; they're about thinking on your feet and tackling puzzles that push your limits.

In this blog, we'll explore some of the trickiest JavaScript questions you might encounter in interviews.

So, whether you're a seasoned developer or just starting out, join us as we navigate through the twists and turns of JavaScript's maze of tricky interview questions. Let's dive in and conquer them together!

source

Tricky JavaScript Interview Questions

Q1: Explain the output of the following code.

var person = {
  name: 'John',
  greet: function() {
    console.log('Hello, ' + this.name);
  }
};

var greetFunc = person.greet;
greetFunc();

Solution:

The output will be Hello, undefined. When greetFunc() is called, it's not called in the context of the person object. Therefore, within the greet function, this refers to the global object (in non-strict mode) or undefined (in strict mode). Since name is not defined in the global scope, this.name evaluates to undefined.

Q2: Describe what happens when you run the following code.

console.log('Start');

setTimeout(function() {
  console.log('Timeout');
}, 0);

Promise.resolve().then(function() {
  console.log('Promise');
});

console.log('End');

Answer: The output will be:

Start
End
Promise
Timeout

This is because setTimeout is placed in the callback queue with a delay of 0 milliseconds, but it's executed after the current synchronous execution context completes. Thus, "Timeout" is logged after "End". However, the Promise callback is executed asynchronously through the microtask queue, so "Promise" is logged before "Timeout".

Difference Between Java And Javascript

Q3: Explain how web workers enable concurrency in JavaScript and how they communicate with the main thread. Discuss the advantages and limitations of using web workers in web applications.

Web workers allow JavaScript code to run in background threads separate from the main UI thread, enabling concurrent execution of tasks. They communicate with the main thread through message passing, allowing data to be exchanged without shared memory. Web workers are beneficial for CPU-intensive tasks and long-running operations but have limitations such as restricted access to the DOM, the inability to directly manipulate the UI, and increased memory overhead due to message passing.

Q4: Describe how Abstract Syntax Trees (AST) are used in JavaScript tooling for code analysis and transformation. Provide an example demonstrating the transformation of code using AST manipulation.

ASTs represent the structure of code as a tree of nodes, with each node representing a syntactic element of the code (e.g., variable declarations, function calls, expressions). JavaScript tooling such as Babel and ESLint utilize ASTs for tasks like transpiling modern JavaScript syntax to older versions, static code analysis, and code transformation/refactoring.

For example, Babel can transform arrow functions to traditional function expressions using AST manipulation.

ES5, ES6, ES7, ES8, ES9: What’s new in each Version of JavaScript

Q5: Convert the following callback-based code into a more readable and maintainable Promise-based code.

getUser(function(user) {
  getProfile(user, function(profile) {
    getPosts(user, function(posts) {
      console.log('User:', user);
      console.log('Profile:', profile);
      console.log('Posts:', posts);
    });
  });
});

Answer: The code converted to Promise-based would look like this:

getUser()
  .then(user => getProfile(user))
  .then(profile => getPosts(user))
  .then(posts => {
    console.log('User:', user);
    console.log('Profile:', profile);
    console.log('Posts:', posts);
  })
  .catch(error => console.error(error));

This creates a more readable and maintainable structure by chaining Promises and handling errors with .catch(). Each then block waits for the previous operation to complete before executing the next one, avoiding the nested callback structure known as "Callback Hell".

Q6: How can you handle errors in asynchronous operations in JavaScript, such as promises or async/await?

When dealing with asynchronous code, errors can occur at a later time after the function has seemingly returned. Here are two ways to handle errors:

Promises: Use .catch() method on a promise to handle any errors that might be thrown during the asynchronous operation.

fetch("https://api.example.com/data")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

Async/await: Use try...catch blocks to wrap your asynchronous code using await.

async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

fetchData();

Top Javascript Interview Questions And Answers

Q7: Deep dive into the Proxy object in JavaScript: how it works, its use cases, and an example demonstrating its utility.

The Proxy object in JavaScript is a powerful feature introduced in ECMAScript 2015 (ES6) that allows developers to create a wrapper for a target object, intercepting and customizing operations like property access, assignment, enumeration, and function invocation.

How it works:
A Proxy object is created with two parameters: the target object and a handler object that defines "traps", methods that provide property access. This enables intercepting actions on the target object, offering opportunities to layer additional behavior over the basic operation (e.g., validation, logging, property watching).

Use cases:

Validation: Ensuring that property values meet certain criteria before setting them.
Logging and profiling: Intercepting operations to log actions or measure performance.
Data binding and observable objects: Automatically updating UI elements or triggering reactions to property changes.
Access control: Restricting access to certain properties based on specific conditions.

Example demonstrating utility:

const validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('Age is not an integer');
      }
      if (value <= 0) {
        throw new RangeError('Age must be a positive integer');
      }
    }
    // The default behavior to store the value
    obj[prop] = value;
    // Indicate success
    return true;
  }
};

const person = new Proxy({}, validator);
person.age = 25; // Works
person.age = 'old'; // Throws TypeError
person.age = -1; // Throws RangeError

Q8: Explain how tagged template literals can be used for HTML escaping and why this approach can be more efficient than traditional string concatenation or templating methods in JavaScript.

function htmlEscape(literals, ...substitutions) {
    let result = "";

    // Interweave the literals with the substitutions
    for (let i = 0; i < substitutions.length; i++) {
        result += literals[i];
        result += substitutions[i]
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
    }

    // Add the final literal
    result += literals[literals.length - 1];
    return result;
}

const userInput = '<script>alert("xss")</script>';
const output = htmlEscape`<div>${userInput}</div>`;

Answer: Tagged template literals allow for safer and potentially more optimized HTML content generation by enabling automatic escaping of injection points within the template literal. This method is more efficient than traditional string concatenation or templating methods because it avoids the overhead of repeatedly parsing the template string and manually escaping each substitution. Additionally, engines like V8 can optimize tagged template literals by caching the parsed template, thus reducing the parsing overhead for templates used in tight loops or frequently called functions.

Q9: Discuss how the temporal dead zone (TDZ) in JavaScript’s block-scoped declarations (let and const) can be exploited in a security-sensitive context to prevent unauthorized access to variables.

(function() {
    if (typeof unauthorizedAccess === "function") {
        throw new Error("Security breach detected!");
    }

    // Temporal Dead Zone for `secret`
    try {
        secret; // ReferenceError: Cannot access 'secret' before initialization
    } catch (e) {
        console.log("Access to 'secret' is restricted until declaration");
    }

    // `secret` is safely declared after the security checks
    let secret = "42";
})();

Answer: The Temporal Dead Zone (TDZ) refers to the period between entering the block scope where a variable is declared (using let or const) and the actual declaration statement. Accessing the variable in this zone results in a ReferenceError. This behavior can be leveraged in security-sensitive contexts to ensure that sensitive variables are not accessed before the security checks are performed, thus preventing unauthorized or premature access. By strategically placing security checks before the declaration of sensitive variables, the TDZ acts as an additional safeguard against tampering or unauthorized access attempts within the scope of a function or block, enhancing the security posture of the code.

Top 55 Java Interview Questions and Answers

Q10: Consider the following JavaScript code snippet

function mysteryFunction() {
  console.log(x); // Step 1
  var x = 20;
  console.log(x); // Step 2
}

x = 10;
mysteryFunction();
console.log(x); // Step 3

Without running the code, answer the following questions:

  1. What is printed at Step 1 and why?

  2. What is printed at Step 2 and why?

  3. What is printed at Step 3 and why?

  4. Considering the scope of x inside mysteryFunction, how does JavaScript's execution context affect the visibility of x outside the function, and what concept is at play here?

Answer:

  1. Step 1: undefined is printed. Even though x is assigned 10 before mysteryFunction is called, the var x = 20; declaration inside the function hoists the declaration of x to the top of mysteryFunction. However, the assignment happens after the console.log(x);, so at the point of logging, x is undefined within the function scope.

  2. Step 2: 20 is printed. Inside mysteryFunction, after the var x = 20; line, the local variable x (specific to mysteryFunction's execution context) is assigned the value 20. This local x shadows any other x variable in the outer scope for the duration of the function's execution.

  3. Step 3: 10 is printed. Outside mysteryFunction, the global variable x (which was assigned 10 before the function call) remains unchanged by the local operations within mysteryFunction. This demonstrates how local variable declarations (using var) inside functions do not affect the value of global variables of the same name.

  4. The concept at play here is scoping and hoisting. JavaScript's function scope means that variables declared with var inside a function are local to that function, regardless of where they are declared within the function body. The var x = 20; declaration (and all var declarations) are hoisted to the top of their containing scope, but their assignments are not. This results in x being undefined when accessed before its assignment within the same scope. The global x remains accessible outside mysteryFunction, demonstrating how different execution contexts (global vs. function scope) maintain separate environments for variables, a fundamental aspect of JavaScript's execution context behavior.

Q11. Consider the following code.

  1. What will be logged to the console first and why?

  2. What will be the output of the console.log(mystery); statement and why?

  3. Explain the role of the then function in the context of the await operator.

function MysteryAsyncFunction() {}

MysteryAsyncFunction.prototype.then = function(onFulfilled) {
  setTimeout(() => onFulfilled("done"), 1000);
};

(async () => {
  const mystery = await new MysteryAsyncFunction();
  console.log(mystery);
})();

console.log(typeof MysteryAsyncFunction().then);

Answer:

  1. The first output will be "function" because the console.log(typeof MysteryAsyncFunction().then); statement executes synchronously and logs the type of the then method defined on MysteryAsyncFunction's prototype, which is a function. This line of code executes before any asynchronous operations.

  2. The output of the console.log(mystery); statement will be "done". This is because the await operator can handle any thenable object, which is an object with a then method. Here, MysteryAsyncFunction creates a thenable due to its prototype having a then method. The await expression waits for the then function to call its onFulfilled callback, which it does via a setTimeout with a delay of 1000 milliseconds. Once the setTimeout callback executes, it resolves the awaited promise with the value "done", which is then logged to the console.

  3. The then function plays a critical role when used with the await operator because await can operate on any object that implements a .then() method (i.e., thenables), not just instances of Promise. In this case, the then function serves as a custom implementation that allows an instance of MysteryAsyncFunction to be treated as a promise-like object. The await operator waits for the then function to invoke its callback (mimicking promise resolution), and the value passed to this callback is treated as the resolved value of the awaited expression.

Q12: What will be the output of console.log(buddy.getName()); and why?

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

function Dog(name) {
  Animal.call(this, name);
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.getName = function() {
  return "Dog: " + Animal.prototype.getName.call(this);
};

const buddy = new Dog("Buddy");
delete buddy.name;
console.log(buddy.getName());

Answer:

The output will be "Dog: undefined". When the name property is deleted from the buddy instance, the getName method falls back to the Animal.prototype.getName method because Dog.prototype.getName calls it explicitly. Since this.name is undefined at this point, the concatenated result is "Dog: undefined"

Q13: What is the exact order of the console log messages and why?

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

async1();

new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});
console.log('script end');

Answer:

The order will be:

  1. script start
  2. async1 start
  3. async2
  4. promise1
  5. script end
  6. async1 end
  7. promise2
  8. setTimeout

This order is determined by the JavaScript event loop and microtask queue processing. Promises and async/await are microtasks which have higher priority and run before the next event loop iteration, while setTimeout callbacks are tasks that run in subsequent iterations.

Q14: What will be the output of both console.log statements and why?

function Obj() {}
Obj.prototype.x = 10;

const obj1 = new Obj();
const obj2 = new Obj();

Obj.prototype = { x: 20 };
const obj3 = new Obj();

console.log(obj1.x, obj2.x, obj3.x);

Obj.prototype.x = 30;
console.log(obj1.x, obj2.x, obj3.x);

Answer:

The output will be:

  1. 10 10 20
  2. 10 10 30

The first set of logs reflects the state before and after the prototype of Obj was reassigned. obj1 and obj2 were created with the original prototype, so they log 10. obj3, created after the prototype reassignment, logs 20. The second set of logs shows that modifying the property on the new prototype affects only instances that inherit from it (obj3), while obj1 and obj2 retain their values because they are linked to the original prototype.

Q15: 

  1. Will the deepClone function work correctly for objects with circular references, such as circularObj? Why or why not?

  2. How does the deepClone function handle circular references?

  3. What will be the outcome of cloning circularObj? Describe any potential issues or limitations with this approach.

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  if (hash.has(obj)) {
    return hash.get(obj);
  }
  let clone = Array.isArray(obj) ? [] : {};
  hash.set(obj, clone);
  Object.keys(obj).forEach(
    key => (clone[key] = deepClone(obj[key], hash))
  );
  return clone;
}

// Example object with circular reference
const circularObj = {
  name: "circle",
  selfRef: null, // This will be set to reference circularObj itself
};
circularObj.selfRef = circularObj;

// Attempt to clone circularObj
const clonedObj = deepClone(circularObj);

Answer:

  1. Yes, the deepClone function provided will work correctly for objects with circular references. It uses a WeakMap to keep track of objects that have already been visited and cloned. When it encounters a circular reference, it returns the previously cloned object instead of trying to clone it again, thus preventing an infinite loop.

  2. The deepClone function handles circular references using the hash parameter, a WeakMap. When it first encounters an object, it stores the object and its clone in the WeakMap. If the same object is encountered again (indicating a circular reference), the function retrieves and returns the cloned object from the WeakMap instead of attempting to clone it again. This mechanism effectively breaks the circular reference in the cloned object.

  3. Cloning circularObj with the deepClone function will produce a deep clone of the original object, including handling the circular reference correctly. The cloned object (clonedObj) will have a selfRef property that correctly points to itself (clonedObj), mirroring the structure of the original circularObj without causing infinite loops or errors. The potential issue with this approach is the use of WeakMap, which cannot be serialized directly. This means that while the function handles circular references effectively in memory, serializing and deserializing such objects (e.g., for network transmission or storage) would require additional handling to preserve the circular structures.

 

Unique tips while facing a JavaScript interview

Unique tips while facing a JavaScript interview

Preparing for a JavaScript interview requires a blend of understanding fundamental concepts, practical problem-solving skills, and staying updated with the latest features and best practices.

Here are some unique tips to help you stand out:

  1. Interviewer Engagement - Engage your interviewer by asking them to provide code snippets or scenarios for you to optimize or refactor. This interactive approach can make you stand out by demonstrating confidence and real-time problem-solving skills.

    Algorithmic Challenges Using Functional Programming - Solve algorithmic challenges using JavaScript's functional programming features, such as map, reduce, filter, and compose. This can demonstrate both your algorithmic thinking and your understanding of functional programming concepts.

  2. Explore JavaScript Engines - Gain a basic understanding of how JavaScript engines like V8 (Chrome, Node.js), SpiderMonkey (Firefox), and JavaScriptCore (Safari) work. Discussing JavaScript optimizations, JIT compilation, and garbage collection can showcase deep knowledge.

  3. Understand the Specification - Dive into the ECMAScript Language Specification. Being able to reference the specification when discussing why JavaScript works the way it does can impress interviewers. For example, discuss how the spec defines the behavior of Array.prototype.reduce() or the intricacies of variable hoisting.

  4. Master the 'this' Keyword in All Contexts - Prepare examples that demonstrate how this behaves in different contexts: in global scope, inside functions, in arrow functions, and when using methods like call, apply, and bind. Offering clear, concise explanations will stand out.

  5. JavaScript Internals and Design Patterns - Discuss JavaScript internals like the Call Stack, Memory Heap, Event Queue, and Event Loop. Understanding design patterns (Singleton, Factory, Observer, etc.) and their use cases in JavaScript specifically can also highlight your deep understanding.

  6. Security Considerations in JavaScript - Discuss common security vulnerabilities in JavaScript applications (e.g., Cross-Site Scripting (XSS), Cross-Site Request Forgery (CSRF), and how to prevent them. Understanding security is often overlooked in interviews but is highly valuable. 

Frequently Asked Questions

OdinSchool | Web Developer Course |FAQ

How hard is JavaScript for beginners?

JavaScript can be both simple and challenging for beginners. Its syntax is straightforward, but concepts like asynchronous programming and DOM manipulation can be tricky. 

JavaScript is a huge thing to learn. What is the quickest way to learn JavaScript as per the industry needs?

The quickest and smarter way to learn all the latest industry-required skills is to join a web developer course.

Can a non-tech learn JavaScript?

Absolutely, a non-tech person can learn JavaScript! In fact, JavaScript is often recommended as one of the first programming languages for beginners due to its versatility and the immediate feedback loop it offers through web development.

Can you make a career in JS?

Yes, you can definitely make a career in JavaScript, just like Jitendra Kumar whose remarkable journey from a humble village in Jharkhand to achieving great success as a Java developer at PeopleTech company and a significant salary increase is nothing short of awe-inspiring.

In fact, JavaScript is one of the most in-demand programming languages in the world of web development, and its popularity continues to grow.

What are the different career paths in the web development world?

Here are several career paths and opportunities that involve JavaScript- Web Development Roadmap (2024): How to Become a Web Developer

 

Share

About the Author

Mechanical engineer turned wordsmith, Pratyusha, holds an MSIT from IIIT, seamlessly blending technical prowess with creative flair in her content writing. By day, she navigates complex topics with precision; by night, she's a mom on a mission, juggling bedtime stories and brainstorming sessions with equal delight.

Join OdinSchool's Full Stack Developer Course

With Job Assistance

View Course