πŸŽ“ Velnov

🟨 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.

Key concepts in this topic

ScopeVariablesFunction scopeBlock scopevar vs letletconstHoistingExecution contextvarTDZDeclarationsTypeErrorLexical scopeClosuresScope chainLookupShadowingGlobal scopewindowGlobalsModulesImplicit globalsStrict mode

Jump to: Easy Β· Medium Β· Hard

Easy questions (2)

Q1. easy

What is 'scope'?

  1. A The region of code where a variable is accessible
  2. B The size of a variable
  3. C The type of a variable
  4. D How long a variable lives in memory

Answer: A β€” The region of code where a variable is accessible

Scope decides where a name can be seen. JavaScript has global scope, function scope, block scope and module scope.

ScopeVariables

Q4. easy

Which declarations are BLOCK-scoped?

  1. A let and const
  2. B var only
  3. C All three
  4. D None

Answer: A β€” let and const

let and const respect { } braces (if, for, while, or a bare block). var is function-scoped and ignores blocks - the main reason modern code avoids it.

letconstBlock scope

Medium questions (16)

Q2. medium

What is the output? function f() { var x = 1; } f(); console.log(x);

  1. A ReferenceError - x is local to f
  2. B 1
  3. C undefined
  4. 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);

  1. A 1 "undefined" - var leaks out of the block, let does not
  2. B 1 2
  3. C ReferenceError
  4. 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?

  1. A JavaScript moving DECLARATIONS to the top of their scope before running the code
  2. B Moving code to the server
  3. C Optimising loops
  4. 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;

  1. A undefined - the declaration is hoisted but the assignment is not
  2. B 5
  3. C ReferenceError
  4. 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() { };
  1. A Yes - you can call them before they are defined in the file
  2. B No, never
  3. C Only in strict mode
  4. 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();
}
  1. A Inner functions can access variables from the scope where they were WRITTEN
  2. B Scope determined by where a function is called
  3. C Global scope only
  4. 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?

  1. A The lookup path: current scope, then enclosing scopes, up to global
  2. B A list of all functions
  3. C The call stack
  4. 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; }

  1. A An inner variable with the same name HIDES the outer one inside that scope
  2. B An error
  3. C The outer value is deleted
  4. 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
  1. A The outermost scope, whose var declarations and function declarations attach to window
  2. B Any function's scope
  3. C The scope inside a module
  4. 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?

  1. A Names can collide between scripts, causing hard-to-trace bugs
  2. B It uses more disk space
  3. C Globals are always slower
  4. 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
  1. A Yes - any pair of braces creates a block scope for let/const
  2. B Only in functions
  3. C No
  4. 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?

  1. A Assigning to an undeclared variable throws instead of creating a global
  2. B It removes function scope
  3. C It makes everything global
  4. 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?

  1. A Their own module scope - top-level declarations are NOT global
  2. B Always global
  3. C Block scope only
  4. 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
  1. A Yes, if it is CALLED after those lines run - scope is about location, timing decides the value
  2. B Never
  3. C Always, immediately
  4. 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?

  1. A Avoid it - use const by default and let when reassignment is needed
  2. B Use var for all loops
  3. C var is required for globals
  4. 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?

  1. A Because module and function scoping keep names private, so renaming is safe
  2. B Because everything is global
  3. C Minifiers avoid variables
  4. 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;

  1. A ReferenceError: Cannot access 'y' before initialization
  2. B undefined
  3. C 5
  4. 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)?

  1. A The span between entering a scope and the let/const declaration, where the variable exists but cannot be touched
  2. B A memory leak
  3. C A slow region of code
  4. 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"); };

  1. A TypeError: hello is not a function - the var is hoisted as undefined
  2. B hi
  3. C ReferenceError
  4. 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();

  1. A ReferenceError - the inner let shadows x for the WHOLE function, creating a TDZ
  2. B 1
  3. C 2
  4. 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);

  1. A 5 - assigning without a keyword creates an accidental GLOBAL
  2. B ReferenceError
  3. C undefined
  4. 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);

  1. A 3 - var leaks out of the loop and keeps its final value
  2. B ReferenceError
  3. C 2
  4. 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));

  1. A 0 1 2 - let creates a fresh binding per iteration
  2. B 3 3 3
  3. C 0 0 0
  4. 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; }
  1. A The whole switch body is ONE block, so a is declared twice
  2. B let is not allowed in switch
  3. C break is missing
  4. 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?

  1. A undefined (in sloppy mode it would be window)
  2. B Always window
  3. C The function itself
  4. 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();

  1. A undefined - the local var x is hoisted, shadowing the global before assignment
  2. B 1
  3. C 2
  4. 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?

  1. A Functions (all declarations) and blocks { } (let/const only), plus modules
  2. B Only functions
  3. C Only blocks
  4. 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);

  1. A 2 - the function modifies the outer variable through the scope chain
  2. B 0
  3. C 1
  4. 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

Practice β€œScope & Hoisting” as a quiz