π¨ JavaScript Β· Topic 12 of 20
Scope & Hoisting β JavaScript Questions with Answers
30 practice questions on Scope & Hoisting, each with the correct answer and a clear explanation of why it is right. Read through them as revision notes, or take this topic as an interactive quiz to test yourself.
2 easy Β· 16 medium Β· 12 hard Β· Part of JavaScript: The language of the web - basics to advanced, 20 topics.
Medium questions (16)
Q2. medium
What is the output?
function f() { var x = 1; }
f();
console.log(x);
- A ReferenceError - x is local to f
- B 1
- C undefined
- D null
Answer: A β ReferenceError - x is local to f
Variables declared inside a function are invisible outside it. This encapsulation is a feature - it prevents name collisions.
Function scope
Q3. medium
What is the output?
if (true) { var a = 1; let b = 2; }
console.log(a, typeof b);
- A 1 "undefined" - var leaks out of the block, let does not
- B 1 2
- C ReferenceError
- D undefined undefined
Answer: A β 1 "undefined" - var leaks out of the block, let does not
var ignores blocks and belongs to the whole function, so a escapes. b is block-scoped, so it does not exist outside - typeof safely reports "undefined" instead of throwing.
Block scopevar vs let
Q5. medium
What is hoisting?
- A JavaScript moving DECLARATIONS to the top of their scope before running the code
- B Moving code to the server
- C Optimising loops
- D Deleting unused variables
Answer: A β JavaScript moving DECLARATIONS to the top of their scope before running the code
During compilation, declarations are registered first. Only the declaration is hoisted, not the assigned value - which is why var variables read as undefined before their assignment line.
HoistingExecution context
Q6. medium
What is the output?
console.log(x);
var x = 5;
- A undefined - the declaration is hoisted but the assignment is not
- B 5
- C ReferenceError
- D null
Answer: A β undefined - the declaration is hoisted but the assignment is not
JavaScript effectively runs 'var x;' first (giving undefined), then 'x = 5' at its original position.
Hoistingvar
Q9. medium
Are FUNCTION DECLARATIONS hoisted with their body?
greet(); // works
function greet() { }
hello(); // TypeError
var hello = function() { };
- A Yes - you can call them before they are defined in the file
- B No, never
- C Only in strict mode
- D Only inside functions
Answer: A β Yes - you can call them before they are defined in the file
Function declarations are fully hoisted, so helper functions can be defined at the bottom of a file. Function EXPRESSIONS follow variable rules and cannot be called early.
HoistingDeclarations
Q11. medium
What is lexical scope?
function outer() {
const msg = "hi";
function inner() { console.log(msg); } // sees msg
inner();
}
- A Inner functions can access variables from the scope where they were WRITTEN
- B Scope determined by where a function is called
- C Global scope only
- D Random scoping
Answer: A β Inner functions can access variables from the scope where they were WRITTEN
JavaScript uses static/lexical scoping: nesting in the source decides visibility, not the call site. This is what makes closures predictable.
Lexical scopeClosures
Q12. medium
What is the scope chain?
- A The lookup path: current scope, then enclosing scopes, up to global
- B A list of all functions
- C The call stack
- D A linked list of objects
Answer: A β The lookup path: current scope, then enclosing scopes, up to global
When a name is used, JavaScript searches outward one level at a time and stops at the first match. Not finding it anywhere gives a ReferenceError.
Scope chainLookup
Q13. medium
What is variable shadowing?
let x = 1;
function f() { let x = 2; }
- A An inner variable with the same name HIDES the outer one inside that scope
- B An error
- C The outer value is deleted
- D Both variables merge
Answer: A β An inner variable with the same name HIDES the outer one inside that scope
The inner x is a separate variable; the outer one is untouched and reappears outside the function. Shadowing is legal but can confuse readers if overused.
ShadowingScope
Q15. medium
What is the global scope in a browser?
var a = 1;
let b = 2;
console.log(window.a); // 1
console.log(window.b); // undefined
- A The outermost scope, whose var declarations and function declarations attach to window
- B Any function's scope
- C The scope inside a module
- D Block scope
Answer: A β The outermost scope, whose var declarations and function declarations attach to window
var x = 1 at top level creates window.x. let/const do NOT attach to window, and ES modules have their own module scope entirely.
Global scopewindow
Q16. medium
Why is polluting the global scope a problem?
- A Names can collide between scripts, causing hard-to-trace bugs
- B It uses more disk space
- C Globals are always slower
- D It is a syntax error
Answer: A β Names can collide between scripts, causing hard-to-trace bugs
Two libraries both defining 'config' would overwrite each other. Modules, IIFEs and block scoping exist to keep names contained.
GlobalsModules
Q20. medium
Do blocks like { } (standalone) create scope for let?
{
let temp = compute();
use(temp);
}
// temp is gone here
- A Yes - any pair of braces creates a block scope for let/const
- B Only in functions
- C No
- D Only in loops
Answer: A β Yes - any pair of braces creates a block scope for let/const
You can use a bare block to limit a temporary variable's lifetime, useful in switch cases to avoid redeclaration errors.
Block scope
Q22. medium
What does 'use strict' change about scope?
- A Assigning to an undeclared variable throws instead of creating a global
- B It removes function scope
- C It makes everything global
- D Nothing
Answer: A β Assigning to an undeclared variable throws instead of creating a global
Strict mode also forbids duplicate parameter names and changes this in plain function calls from window to undefined. ES modules and class bodies are strict automatically.
Strict mode
Q24. medium
What scope do ES modules have?
- A Their own module scope - top-level declarations are NOT global
- B Always global
- C Block scope only
- D No scope
Answer: A β Their own module scope - top-level declarations are NOT global
Each module file has a private scope; you share values deliberately with export/import. Modules are also strict mode by default - a major reason to prefer them.
Modulesimport/export
Q26. medium
Can a function access variables declared AFTER it in the same scope?
function show() { console.log(msg); }
let msg = "hi";
show(); // "hi" - called after msg exists
- A Yes, if it is CALLED after those lines run - scope is about location, timing decides the value
- B Never
- C Always, immediately
- D Only with var
Answer: A β Yes, if it is CALLED after those lines run - scope is about location, timing decides the value
The function body is evaluated at CALL time, so by then later declarations have executed. Calling it too early gives undefined (var) or a TDZ error (let).
ScopeExecution order
Q28. medium
What is the practical advice about var today?
- A Avoid it - use const by default and let when reassignment is needed
- B Use var for all loops
- C var is required for globals
- D var is faster
Answer: A β Avoid it - use const by default and let when reassignment is needed
var's function scoping and silent hoisting cause bugs that let/const prevent. You still need to RECOGNISE var in older code, but there is no reason to write it in new code.
Best practices
Q30. medium
Why does minified/bundled code rarely break due to scope?
- A Because module and function scoping keep names private, so renaming is safe
- B Because everything is global
- C Minifiers avoid variables
- D It often does break
Answer: A β Because module and function scoping keep names private, so renaming is safe
Tools can safely shorten local names precisely because scope guarantees nothing outside references them - another practical benefit of avoiding globals.
ModulesTooling
Hard questions (12)
Q7. hard
What is the output?
console.log(y);
let y = 5;
- A ReferenceError: Cannot access 'y' before initialization
- B undefined
- C 5
- D null
Answer: A β ReferenceError: Cannot access 'y' before initialization
let and const ARE hoisted, but sit in the Temporal Dead Zone until their declaration line - accessing them there throws. This surfaces bugs that var would have hidden as undefined.
TDZlet
Q8. hard
What is the Temporal Dead Zone (TDZ)?
- A The span between entering a scope and the let/const declaration, where the variable exists but cannot be touched
- B A memory leak
- C A slow region of code
- D An old browser feature
Answer: A β The span between entering a scope and the let/const declaration, where the variable exists but cannot be touched
The TDZ is why let/const feel 'not hoisted' - they are, but access is forbidden until initialised. It catches use-before-declare mistakes early.
TDZHoisting
Q10. hard
What is the output?
hello();
var hello = function() { console.log("hi"); };
- A TypeError: hello is not a function - the var is hoisted as undefined
- B hi
- C ReferenceError
- D undefined
Answer: A β TypeError: hello is not a function - the var is hoisted as undefined
At call time hello holds undefined, and calling undefined throws a TypeError. Note the ERROR TYPE differs from a truly undeclared name (ReferenceError) - useful for diagnosing.
HoistingTypeError
Q14. hard
What is the output?
let x = 1;
function f() { console.log(x); let x = 2; }
f();
- A ReferenceError - the inner let shadows x for the WHOLE function, creating a TDZ
- B 1
- C 2
- D undefined
Answer: A β ReferenceError - the inner let shadows x for the WHOLE function, creating a TDZ
Because f declares its own x, every reference inside f refers to the LOCAL x - which is in its TDZ on the console.log line. Shadowing applies to the entire block, not just after the declaration.
TDZShadowing
Q17. hard
What is the output (non-strict mode)?
function f() { undeclared = 5; }
f();
console.log(undeclared);
- A 5 - assigning without a keyword creates an accidental GLOBAL
- B ReferenceError
- C undefined
- D SyntaxError
Answer: A β 5 - assigning without a keyword creates an accidental GLOBAL
Sloppy mode silently creates a global, which is why 'use strict' (and modules) turn this into a ReferenceError. Always declare with const or let.
Implicit globalsStrict mode
Q18. hard
What is the output?
for (var i = 0; i < 3; i++) { }
console.log(i);
- A 3 - var leaks out of the loop and keeps its final value
- B ReferenceError
- C 2
- D undefined
Answer: A β 3 - var leaks out of the loop and keeps its final value
var is function-scoped, so the counter survives the loop. With let, i exists only inside the loop and this line would throw - usually what you want.
varletLoops
Q19. hard
What is the output?
for (let i = 0; i < 3; i++) setTimeout(() => console.log(i));
- A 0 1 2 - let creates a fresh binding per iteration
- B 3 3 3
- C 0 0 0
- D undefined
Answer: A β 0 1 2 - let creates a fresh binding per iteration
Each iteration gets its own i, so each callback captures a different value. With var, all three would share one binding and print 3 3 3.
ClosuresletLoops
Q21. hard
Why does this throw in a switch?
switch (x) { case 1: let a = 1; break; case 2: let a = 2; }
case 1: { let a = 1; break; }
case 2: { let a = 2; break; }
- A The whole switch body is ONE block, so a is declared twice
- B let is not allowed in switch
- C break is missing
- D It does not throw
Answer: A β The whole switch body is ONE block, so a is declared twice
All cases share a single block scope. Wrap each case body in its own braces { } to give each a private scope.
switchBlock scope
Q23. hard
In a regular (non-method) function call in strict mode, what is this?
- A undefined (in sloppy mode it would be window)
- B Always window
- C The function itself
- D null
Answer: A β undefined (in sloppy mode it would be window)
Strict mode stops this from silently defaulting to the global object, which prevents accidental global property writes inside functions.
thisStrict mode
Q25. hard
What is the output?
var x = 1;
function f() { console.log(x); var x = 2; }
f();
- A undefined - the local var x is hoisted, shadowing the global before assignment
- B 1
- C 2
- D ReferenceError
Answer: A β undefined - the local var x is hoisted, shadowing the global before assignment
The function's own x is hoisted to the top as undefined, so the console.log sees the local (not the global 1). This confusing behaviour is exactly what let's TDZ replaced with a clear error.
HoistingShadowingvar
Q27. hard
What creates a new scope in JavaScript?
- A Functions (all declarations) and blocks { } (let/const only), plus modules
- B Only functions
- C Only blocks
- D Every statement
Answer: A β Functions (all declarations) and blocks { } (let/const only), plus modules
Functions create scope for everything including var. Blocks create scope only for let/const/class. Modules add a file-level scope.
Scope types
Q29. hard
What is the output?
let count = 0;
function inc() { count++; }
inc(); inc();
console.log(count);
- A 2 - the function modifies the outer variable through the scope chain
- B 0
- C 1
- D ReferenceError
Answer: A β 2 - the function modifies the outer variable through the scope chain
Functions can READ and WRITE outer variables (unlike creating a new local, which needs a declaration keyword). This is the basis of closures holding state.
Scope chainClosures