Welcome to our JavaScript Interview Series! Hoisting, scope, and closures are foundational concepts in JavaScript that are almost guaranteed to come up in any technical interview. Understanding them isn't just about passing the interview; it's about writing better, more efficient code. This guide will walk you through 10 essential questions, complete with standard answers and tricky follow-ups to help you ace the test.
1. What will be logged to the console in the following code snippet?
console.log(myVar);
var myVar = 10;
Core Concept Tested: Hoisting with var
.
Standard Answer: The console will log undefined
. In JavaScript, declarations made with var
are "hoisted" to the top of their scope during the compilation phase. This means the JavaScript engine processes the declaration var myVar;
first. However, the initialization (= 10
) happens exactly where you wrote it. So, when console.log(myVar)
is executed, the variable has been declared but not yet assigned a value, resulting in undefined
.
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- How would the output change if we used
let
orconst
instead ofvar
? - Can you explain the difference between the compilation phase and the execution phase in the context of hoisting?
- How does hoisting affect function declarations versus function expressions?
2. What is the "Temporal Dead Zone" (TDZ) and how does it relate to let
and const
?
Core Concept Tested: Hoisting with ES6 variables (let
and const
).
Standard Answer: The Temporal Dead Zone is a term for the period between entering a scope and where a let
or const
variable is declared. While declarations for let
and const
are also hoisted, they are not initialized with undefined
like var
is. Accessing a let
or const
variable before its declaration results in a ReferenceError
. This behavior enforces better coding practices by preventing you from using a variable before it's officially declared.
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- Can you provide a code example that demonstrates the TDZ?
- Why was the TDZ introduced in ES6? What problem does it solve?
- Does the TDZ apply to function parameters with default values?
3. What is the difference in output between these two function calls?
sayHello(); // Works
sayGoodbye(); // Throws TypeError
// Function Declaration
function sayHello() {
console.log('Hello!');
}
// Function Expression
var sayGoodbye = function() {
console.log('Goodbye!');
};
Core Concept Tested: Hoisting of Function Declarations vs. Function Expressions.
Standard Answer: The entire function declaration sayHello
is hoisted, including its body. This means you can call it before it appears in the code. However, for the function expression sayGoodbye
, only the variable declaration (var sayGoodbye;
) is hoisted. When sayGoodbye()
is called, the variable exists but is undefined
, and you cannot invoke undefined
as a function, which results in a TypeError
.
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- What if
sayGoodbye
was declared withlet
orconst
? What error would be thrown and why? - What are the main advantages of using function expressions over function declarations?
- How do Immediately Invoked Function Expressions (IIFEs) relate to this concept?
4. Can you explain the different types of scope in JavaScript?
Core Concept Tested: Understanding of JavaScript Scopes.
Standard Answer: JavaScript has three main types of scope:
- Global Scope: Variables declared outside of any function or block are in the global scope. They can be accessed from anywhere in the application.
- Function Scope: Variables declared with
var
inside a function are only accessible within that function. - Block Scope: Introduced with ES6, variables declared with
let
andconst
inside a block (e.g., within anif
statement or afor
loop, denoted by{}
) are only accessible within that block.
This distinction is crucial for managing variables and avoiding naming conflicts.
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- What is the "scope chain" and how does the JavaScript engine use it to find variables?
- How can you create a private variable in JavaScript, and which scope is key to this pattern?
- What is lexical scoping (or static scoping)?
5. What will this code log to the console and why?
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
Core Concept Tested: Closures and Asynchronous Behavior in Loops.
Standard Answer: This code will log the number 3
three times. This is a classic closure problem. The setTimeout
callback function is executed after the loop has completed. Because var
is function-scoped, there is only one i
variable in the scope, and its final value is 3
. By the time the callbacks execute, they all reference this same i
, which is now 3
.
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- How could you modify this code to log
0
,1
, and2
? - Explain how using
let
instead ofvar
in the loop fixes this issue. - Can you solve this problem using an Immediately Invoked Function Expression (IIFE)?
6. Explain what a closure is in your own words.
Core Concept Tested: Definition and Understanding of Closures.
Standard Answer: A closure is a function that remembers the environment in which it was created. More specifically, it's a function that has access to its own scope, the outer function's scope, and the global scope. This means a closure can access variables from its outer (enclosing) function, even after that outer function has finished executing. It "closes over" the variables from its lexical scope.
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- Can you give a practical, real-world example of when you would use a closure?
- How do closures impact memory usage in an application? Can they cause memory leaks?
- What is the relationship between closures and callbacks?
7. How can you use a closure to create a private counter?
Core Concept Tested: Practical Application of Closures (Data Encapsulation).
Standard Answer: You can create a private counter by defining a variable within an outer function and returning an inner function (or an object with methods) that has access to that variable. The outer world cannot access the count
variable directly, only through the methods exposed by the closure.
function createCounter() {
let count = 0; // This is a private variable
return {
increment: function() {
count++;
console.log(count);
},
getValue: function() {
return count;
}
};
}
const counter = createCounter();
counter.increment(); // logs 1
counter.increment(); // logs 2
console.log(counter.count); // undefined - cannot access directly
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- This pattern is related to the "Module Pattern." Can you explain what that is?
- What are the benefits of data encapsulation and making variables private?
- How could you extend this to include a
decrement
function?
8. Predict the output of the following code:
let a = 1;
function foo() {
console.log(a);
}
function bar() {
let a = 2;
foo();
}
bar();
Core Concept Tested: Lexical Scoping.
Standard Answer: The output will be 1
. JavaScript uses lexical scoping, which means the scope of a function is determined by where it is defined in the source code, not where it is called. The function foo
is defined in the global scope, where a
is 1
. When foo
is called inside bar
, it doesn't look at bar
's scope for the value of a
. It looks at its own lexical scope, which is the global scope. Therefore, it logs the global a
.
Potential Follow-up Questions:(π Want to test your skills? Try a Mock Interviews)
- What is dynamic scoping, and does JavaScript ever use it?
- How would you change this code so that
foo
logs the value ofa
from insidebar
? - Can you explain how the scope chain works in this specific example?
9. What is an IIFE and why might you use one?
Core Concept Tested: Immediately Invoked Function Expressions and Scope Management.
Standard Answer: An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined. It's created by wrapping a function expression in parentheses, and then immediately calling it with another set of parentheses.
(function() {
var message = "I am private";
console.log(message); // "I am private"
})();
console.log(typeof message); // "undefined"
The primary reason to use an IIFE is to create a new scope. This helps avoid polluting the global scope and is a classic way to achieve data privacy before the introduction of let
and const
.
Potential Follow-up Questions:
- Can you pass arguments into an IIFE? Show me how.
- How were IIFEs used to solve the
var
-in-a-loop problem we discussed earlier? - With the introduction of modules and block-scoped variables (
let
,const
), are IIFEs still relevant?
10. Predict the output of this final code snippet:
var x = 10;
function test() {
var x = 20;
function inner() {
console.log(x);
}
return inner;
}
var myFunc = test();
myFunc();
Core Concept Tested: Combination of Scope, Closures, and Execution Context.
Standard Answer: The output will be 20
. The test
function is called, which creates a local variable x
with the value 20
. It then defines and returns the inner
function. Because inner
is defined within the scope of test
, it forms a closure over test
's scope. When myFunc
(which is a reference to inner
) is executed, it still has access to its lexical scope, where x
is 20
. It does not look at the global x
.
Potential Follow-up Questions:
- What would happen if the line
var x = 20;
was removed from thetest
function? - If we changed
var
tolet
in all places, would the output change? Why or why not? - Can you walk me through the creation of the execution contexts and the scope chain as this code runs?
Top comments (0)