Essential JavaScript Questions: Common Mistakes and How to Avoid Them in Interviews
As a senior JavaScript developer, you’re likely familiar with the pressure of technical interviews. Despite years of experience, even seasoned developers often trip up on fundamental questions that seem deceptively simple. Interviewers ask these questions to test not only your knowledge but also your understanding of core JavaScript principles. In this article, we’ll dive into some of the most common JavaScript questions, explore the underlying concepts, and discuss strategies to avoid common pitfalls during interviews.
1. Understanding Hoisting: var
, let
, and const
One of the classic interview topics is hoisting, where variables are moved to the top of their scope before code execution. However, the behavior differs depending on whether you use var
, let
, or const
.
var
: Hoisted and initialized withundefined
.
console.log(x); // undefined
var x = 10;
let
and const
: Hoisted but not initialized. They are in a temporal dead zone, and accessing them before the declaration causes a ReferenceError.
console.log(y); // ReferenceError
let y = 20;
console.log(z); // ReferenceError
const z = 30;
Pro Tip: Take your time when responding to hoisting questions. It’s easy to confuse var
, let
, and const
, so carefully explain how hoisting impacts their behavior.
2. Currying Functions
Currying is a functional programming technique often used in JavaScript interviews. It transforms a function with multiple arguments into a series of functions, each taking one argument.
- Example:
function multiply(a) {
return function(b) {
return a * b;
}
}
const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(5)); // 10
Currying allows for partial application, meaning you can create specialized functions based on preset arguments. Interviewers love asking about currying as it demonstrates your understanding of higher-order functions and functional programming.
3. Mutability with const
A common trap is the assumption that const
variables are immutable. In fact, const
only ensures that the binding is immutable, meaning the reference to the variable cannot be changed, but the contents of objects or arrays assigned to const
can still be modified.
- Example:
const person = { name: "John" };
person.name = "Doe"; // This is allowed
console.log(person); // { name: "Doe" }
person = {}; // Error: Assignment to constant variable.
Understanding the nuances of const
is critical, as interviewers may try to confuse you by asking whether objects declared with const
can be mutated.
4. Closures:
Closures are an essential JavaScript concept, and interviewers frequently ask about them to test your knowledge of scope. A closure occurs when a function remembers its lexical scope, even when the function is executed outside that scope.
- Example:
function outer() {
let count = 0;
return function inner() {
count++;
console.log(count);
}
}
const counter = outer();
counter(); // 1
counter(); // 2
Closures are useful for creating private variables and maintaining state. In interviews, you might be asked to explain how closures work or to write functions that utilize closures effectively.
5. Event Loop and Asynchronous JavaScript:
Questions about the event loop and asynchronous behavior are common, especially as JavaScript is single-threaded but can handle asynchronous operations through callbacks, promises, and async/await.
- Example:
console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("End");
// Output:
// Start
// End
// Promise
// Timeout
Here, the event loop processes the synchronous code first (logs “Start” and “End”). Then, it handles the microtask queue (the resolved promise), followed by the macrotask queue (setTimeout).
Common Interview Question:
- “Explain the difference between the call stack, task queue, and microtask queue in the context of the event loop.”
Understanding how JavaScript handles asynchronous operations will help you explain tricky scenarios that often confuse interviewees.
6. Pass by Value vs. Pass by Reference
Another area where interviewers can confuse candidates is the distinction between pass by value and pass by reference.
- Primitive Types (Pass by Value): When you pass a primitive value (like a number or string), a copy of the value is passed to the function.
let a = 10;
function modify(x) {
x = 20;
}
modify(a);
console.log(a); // 10
Reference Types (Pass by Reference): When passing objects or arrays, the reference to the object is passed, allowing changes to affect the original object.
let obj = { name: "Alice" };
function modify(o) {
o.name = "Bob";
}
modify(obj);
console.log(obj.name); // Bob
Common Pitfall: Mistaking how reference types behave when passed into functions. Always clarify that objects are passed by reference, but const
-bound references themselves cannot be reassigned.
7. Prototype and Inheritance
Prototypes are fundamental to JavaScript’s object-oriented nature, and many interviewers ask about how inheritance works via prototypes. Each object in JavaScript has a [[Prototype]]
(accessed via __proto__
or Object.getPrototypeOf
), which links objects and allows for shared properties and methods.
- Example:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};
const john = new Person("John");
john.greet(); // Hello, my name is John
You may be asked to explain how JavaScript inheritance works, how the prototype chain resolves properties, or to implement inheritance using class
syntax.
8. Deep vs. Shallow Copy:
Understanding the difference between deep and shallow copies is crucial, especially when dealing with objects and arrays.
- Shallow Copy: Copies the references to the nested objects.
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
shallowCopy.b.c = 42;
console.log(original.b.c); // 42
Deep Copy: Creates a true independent copy of the entire object structure.
const deepCopy = JSON.parse(JSON.stringify(original));
Common Interview Question: “What’s the difference between a deep copy and a shallow copy? How would you implement a deep copy?”
9. Is const
immutable? Can the key-value pairs of an object declared with const
be updated?
Common Interview Trap: Many developers assume that const
-declared variables are completely immutable. However, the immutability in const
refers only to the binding of the variable, not the contents of an object or array.
- Immutable Binding: Once a
const
variable is assigned, it cannot be reassigned to a new value. - Mutable Contents: The properties of an object declared with
const
can still be modified.
Example:
const car = { brand: "Toyota", model: "Camry" };
// Changing a property is allowed
car.model = "Corolla";
console.log(car); // { brand: "Toyota", model: "Corolla" }
// Reassigning the object will throw an error
car = { brand: "Honda", model: "Civic" }; // Error: Assignment to constant variable.
This is a key distinction, especially when handling objects or arrays. The reference is fixed, but the contents can still be altered.
Follow-up Interview Questions:
- How would you make an object truly immutable in JavaScript?
- One way to achieve immutability is by using
Object.freeze()
, which prevents modifications to the object’s properties. - Example:
const frozenCar = Object.freeze({ brand: "Toyota", model: "Camry" });
frozenCar.model = "Corolla"; // This change will be silently ignored in non-strict mode
console.log(frozenCar); // { brand: "Toyota", model: "Camry" }
2. How would you handle deep immutability for nested objects?
Object.freeze()
only applies to the top-level properties. For deep immutability, you would need to recursively freeze each property in nested objects.
10. How does JavaScript handle function declarations vs. function expressions?
Common Mistake: Developers often confuse function declarations and function expressions, especially when it comes to hoisting.
- Function Declaration: These are hoisted, meaning you can call the function before it is defined in the code.
greet(); // This works
function greet() {
console.log("Hello!");
}
Function Expression: These are not hoisted. If you try to call a function expression before it’s assigned, you will get an error.
greet(); // Error: greet is not a function
const greet = function() {
console.log("Hello!");
};
Follow-up Interview Question:
- What’s the difference between
var
andlet
/const
when it comes to function expressions? - With
var
: The variable is hoisted but initialized asundefined
, so trying to call it results inundefined is not a function
. - With
let
/const
: The variable is in a temporal dead zone and not accessible until it is initialized, which throws a ReferenceError.
11. What will this output and why?
console.log(0.1 + 0.2 === 0.3); // false
Common Confusion: Many developers expect the result to be true
because they assume 0.1 + 0.2 should equal 0.3. However, JavaScript uses floating-point arithmetic, which leads to precision issues.
- Explanation: The result of
0.1 + 0.2
is actually0.30000000000000004
due to how numbers are stored in memory.
console.log(0.1 + 0.2); // 0.30000000000000004
Follow-up Interview Question:
- How would you reliably compare floating-point numbers?
- One approach is to use a small epsilon value to compare the difference:
const epsilon = Number.EPSILON;
console.log(Math.abs(0.1 + 0.2 - 0.3) < epsilon); // true
12. What does the typeof
operator return for different data types?
Common Interview Question: The typeof
operator can sometimes return unexpected results, especially when dealing with certain data types.
- Basic Example:
console.log(typeof "Hello"); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
. Surprising Cases:
console.log(typeof null); // "object" (this is a known bug in JavaScript)
console.log(typeof []); // "object"
console.log(typeof {}); // "object"
console.log(typeof function(){}); // "function"
Follow-up Interview Question:
- How can you distinguish between arrays and objects, given that
typeof []
returns"object"
? - Use
Array.isArray()
to correctly identify arrays:
console.log(Array.isArray([])); // true
13. What is the output of the following code?
console.log([] + []); // ""
console.log([] + {}); // "[object Object]"
console.log({} + []); // 0
Explanation:
[] + []
: Empty arrays are implicitly converted to empty strings when used in arithmetic, so the result is an empty string""
.[] + {}
: The empty array becomes""
and the object is converted to[object Object]
, resulting in"[object Object]"
.{} + []
: If you write{}
at the beginning of a line, JavaScript interprets it as a block of code (not an object literal), so the addition of[]
is treated as0
.
Follow-up Interview Question:
- Why does
[] + {}
return[object Object]
but{}
+[]
return0
? - This is due to how JavaScript interprets the braces
{}
. When used at the start of a line,{}
is treated as a block of code rather than an object.
Conclusion
The key to acing JavaScript interviews lies in mastering these foundational concepts and being able to articulate them clearly. Don’t let “basic” questions trip you up — take the time to explain your thought process, and be prepared to demonstrate your understanding through code examples. With enough practice and clarity, you’ll be able to tackle any JavaScript interview confidently.