CareerKit

JavaScript Output Questions with Answers

"What does this print?" questions from JavaScript output rounds, covering hoisting, closures, this, coercion and the event loop. Every output shown was produced by running the code.

10 free answers from our JavaScript Output Questions ebook, which has all 100 questions.

Hoisting and the temporal dead zone

var used before its declaration

Code
function show() {
  console.log(score);
  var score = 90;
  console.log(score);
}
show();
Output
undefined
90

var score is hoisted to the top of the function and initialised with undefined. Only the assignment = 90 stays where it's written.

let used before its declaration

Code
function show() {
  console.log(marks);
  let marks = 85;
}
show();
Output
ReferenceError: Cannot access 'marks' before initialization

let is hoisted too, but it stays uninitialised in the temporal dead zone until its line runs. Reading it early throws instead of giving undefined.

Closures inside loops

setTimeout in a var loop

Code
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
Output
3
3
3

All three callbacks close over the same var i. They run after the loop ends, when i is 3.

setTimeout in a let loop

Code
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
Output
0
1
2

let creates a new binding for each iteration, so each callback has its own i.

this in different contexts

A normal method call

Code
const course = {
  title: "React",
  describe() {
    return `Course: ${this.title}`;
  },
};
console.log(course.describe());
Output
Course: React

Called as course.describe(), so this is the object before the dot: course.

A method copied into a variable (strict mode)

Code
"use strict";
const course = {
  title: "React",
  describe() {
    return this.title;
  },
};

const describe = course.describe;
try {
  console.log(describe());
} catch (error) {
  console.log(error.name + ": " + error.message);
}
Output
TypeError: Cannot read properties of undefined (reading 'title')

describe() is a plain call with nothing before the dot. In strict mode (and in all modules and classes), this is then undefined.

Type coercion surprises

An array plus an object

Code
console.log([] + {});
console.log(String([]), String({}));
Output
[object Object]
 [object Object]

+ with objects converts both to primitives (strings here): [] becomes "" and {} becomes "[object Object]". The empty string plus that is just "[object Object]". The second line starts with an empty string, which is why there's a leading space.

Empty arrays and equality

Code
console.log([] == ![]);
console.log([] == []);
console.log([] == "");
Output
true
false
true

![] is false (arrays are truthy). Then [] == false converts both sides to numbers: [] becomes "" then 0, and false becomes 0, so 0 == 0. Two separate arrays are different objects, so [] == [] is false.

Promises and setTimeout ordering

The basic order

Code
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");
Output
A
D
C
B

Synchronous code runs first (A, D). Then the microtask queue (promise callbacks: C), and only then the next macrotask (the timer: B).

A promise executor runs synchronously

Code
console.log("1");
new Promise((resolve) => {
  console.log("2");
  resolve();
  console.log("3");
}).then(() => console.log("5"));
console.log("4");
Output
1
2
3
4
5

The function passed to new Promise runs immediately. resolve() doesn't stop it, so 3 still prints. Only the .then callback waits for the microtask queue.

All 100 questions in the ebook

Linked questions are answered free on this page; the rest are in the ebook.

Hoisting and the temporal dead zone

  1. var used before its declaration
  2. let used before its declaration
  3. A function declaration called before it's written
  4. A function expression called before it's assigned
  5. An arrow function in a const, called early
  6. A local var hides a global one
  7. typeof cannot rescue the temporal dead zone
  8. Same name for a var and a function
  9. Two function declarations with the same name
  10. var inside a block that never runs
  11. The loop variable after a var loop
  12. A class used before its declaration
  13. Default parameters referring to a later parameter
  14. A let that reads itself in its own initialiser
  15. Redeclaring a parameter with var
  16. A named function expression's name
  17. Reassigning a const versus mutating it
  18. A function that returns before its helper is defined
  19. A var and a let with the same name in nested scopes
  20. Hoisting inside a nested function

Closures inside loops

  1. setTimeout in a var loop
  2. setTimeout in a let loop
  3. Fixing the var loop with an IIFE
  4. Fixing the var loop with setTimeout's extra arguments
  5. Fixing the var loop with bind
  6. An array of functions created in a var loop
  7. The same array of functions with let
  8. A let declared outside the loop
  9. A closure captures the variable, not the value
  10. Capturing a snapshot instead
  11. Two counters from the same factory
  12. forEach callbacks with var outside
  13. Increasing delays with a var loop
  14. A while loop with a let inside the body
  15. for...of with const
  16. A shared module-level counter
  17. Simulated click handlers in a var loop
  18. Closures with an async loop
  19. A closure keeping a cache alive
  20. Closures and the loop condition

this in different contexts

  1. A normal method call
  2. A method copied into a variable (strict mode)
  3. A method called through another object
  4. A regular function nested inside a method
  5. An arrow function nested inside a method
  6. An arrow function used as a method
  7. call and apply with different objects
  8. Binding a function twice
  9. call on an arrow function
  10. new with a bound function
  11. Extracting a class method
  12. An arrow function class field
  13. map with a thisArg
  14. this inside a getter
  15. this in a prototype method
  16. this in a static method
  17. Borrowing an array method
  18. A constructor that returns an object
  19. Method chaining that forgets to return this
  20. A callback passed to another object's method

Type coercion surprises

  1. An array plus an object
  2. Empty arrays and equality
  3. baNaNa
  4. Adding null and undefined
  5. Minus minus
  6. An array compared with a string
  7. null compared with 0
  8. Comparing NaN
  9. typeof surprises
  10. Floating point maths
  11. Comparing strings that look like numbers
  12. Sorting numbers without a compare function
  13. parseInt with unexpected input
  14. Booleans from strings
  15. Concatenating with null
  16. Math.max with no arguments
  17. String times string
  18. Comparing objects
  19. Adding numbers and strings in a template
  20. The comma operator and empty values

Promises and setTimeout ordering

  1. The basic order
  2. A promise executor runs synchronously
  3. A timer inside a then
  4. A then inside a timer
  5. Chained then callbacks interleave
  6. await pauses only its own function
  7. Two async functions taking turns
  8. queueMicrotask versus then
  9. setTimeout delays
  10. finally passes the value through
  11. A catch in the middle of a chain
  12. Several then calls on the same promise
  13. A non-function passed to then
  14. Returning a value versus returning a promise
  15. Promise.race with an already-resolved value
  16. An error thrown inside a timer
  17. Async functions and return values
  18. await inside a loop with a timer
  19. A thenable object
  20. Microtasks queued by microtasks