JavaScript Interview Mock sample questions with answers

10 questions from the JavaScript Interview Mock practice bank, spread across its domains. Pick your answer, then open the explanation to see why each option is right or wrong.

  1. Question 1Asynchronous JavaScript

    fetchJson() returns a promise that rejects. What does getData() produce?

    async function getData() {
      try {
        return fetchJson();
      } catch (e) {
        return 'fallback';
      }
    }
    • A

      A promise that fulfils with "fallback"

    • B

      A promise that rejects with fetchJson's error, because the try block finished before the rejection

    • C

      A promise that fulfils with the rejected promise itself

    • D

      A synchronous exception that escapes from getData()

    Show the answer and explanation

    Answer: B

    return fetchJson() hands the pending promise back as the async function's result and leaves the try block at once. The later rejection flows into getData()'s promise and bypasses catch. Writing return await fetchJson() keeps the function suspended inside the try, so the rejection is thrown there and caught. This is the main case where return await is required.

    Why the other options are wrong

    • A. The catch block only sees errors raised while the try block runs, and none is raised there.

    • C. An async function adopts a returned promise; it never fulfils with one.

    • D. Async functions report every failure through their returned promise.

  2. Question 2Types, Values and Objects

    Which loose comparisons evaluate to true? (Choose two.)

    Choose 2.

    • A

      "" == 0

    • B

      "0" == false

    • C

      null == 0

    • D

      undefined == 0

    • E

      "false" == false

    Show the answer and explanation

    Answer: A and B

    In ==, a boolean always converts to a number first (false becomes 0, true becomes 1), and a string compared with a number converts with ToNumber. So "0" == false is "0" == 0, which is 0 == 0. But "false" converts to NaN, so "false" == false is false. null and undefined are only ever == to each other.

    Why the other options are wrong

    • C. null only loosely equals undefined; it is never converted to a number here.

    • D. undefined only loosely equals null.

    • E. false becomes 0, but "false" converts to NaN, and NaN equals nothing.

  3. Question 3Functions and Data

    What does this log?

    const a = [1, , 3];
    const doubled = a.map(x => x * 2);
    console.log(doubled.length, Object.keys(doubled));
    • A

      2 ['0', '1'], because map drops the hole

    • B

      3 ['0', '2']

    • C

      3 ['0', '1', '2'], because the hole becomes NaN

    • D

      3 ['0', '1', '2'], because the hole becomes undefined

    Show the answer and explanation

    Answer: B

    Most ES5 array methods (forEach, map, filter, some, every, reduce) skip holes. map creates a result of the same length and simply doesn't define the skipped index, so the hole survives. Object.keys lists only the defined indices, '0' and '2'.

    Why the other options are wrong

    • A. map keeps the array's length; it doesn't compact anything.

    • C. The callback never runs for the hole, so no NaN is produced.

    • D. map preserves holes; it does not fill them in.

  4. Question 4Browser and Interview Coding

    The page contains <input id="name" value="hello">. The user changes the text to world. What do input.getAttribute('value') and input.value return?

    • A

      'world' and 'world', since the attribute tracks what the user typed

    • B

      'hello' and 'world'

    • C

      'world' and 'hello', because the property keeps the initial markup value

    • D

      null and 'world', because the attribute is cleared once the user edits

    Show the answer and explanation

    Answer: B

    For form controls, attributes describe the initial state and properties the current state. value and checked properties are live, while the value and checked attributes map to defaultValue and defaultChecked. That is why form.reset() can restore the original values.

    Why the other options are wrong

    • A. Typing changes the property only; the attribute keeps its original markup value.

    • C. It is the reverse: the property is live and the attribute is the default.

    • D. The attribute stays in the DOM exactly as written.

  5. Question 5Modern Language and the Engine

    What does this log?

    const target = {
      _name: 'target',
      get name() { return this._name; },
    };
    const proxy = new Proxy(target, {
      get(t, key, receiver) {
        if (key === '_name') return 'proxy';
        return t[key];
      },
    });
    console.log(proxy.name);
    • A

      'target'

    • B

      'proxy'

    • C

      undefined

    • D

      A TypeError, because the trap ignores its receiver

    Show the answer and explanation

    Answer: A

    proxy.name hits the trap with key name. t[key] reads the getter on the target with this === target, so this._name is a plain read of 'target' that bypasses the proxy. Writing return Reflect.get(t, key, receiver) passes the proxy as this, so this._name goes back through the trap and returns 'proxy'. Forwarding with Reflect and the receiver keeps getters, setters and inheritance consistent.

    Why the other options are wrong

    • B. That needs Reflect.get(t, key, receiver), which runs the getter with this as the proxy.

    • C. The getter does run, and it finds _name on the target.

    • D. Ignoring receiver is allowed; it only changes which this the getter sees.

  6. Question 6Asynchronous JavaScript

    Which statement about this concurrency-limited map is correct?

    async function mapLimit(items, limit, fn) {
      const results = [];
      let i = 0;
      async function worker() {
        while (i < items.length) {
          const idx = i++;
          results[idx] = await fn(items[idx]);
        }
      }
      await Promise.all(Array.from({ length: limit }, worker));
      return results;
    }
    • A

      Two workers can read the same i at once, so some items are processed twice

    • B

      results ends up in completion order, not input order

    • C

      It runs at most limit calls at once and keeps input order, but one rejection rejects the call while other workers keep going

    • D

      It starts every item at once, because Array.from calls worker for each element of items

    Show the answer and explanation

    Answer: C

    Each worker claims the next index synchronously, then awaits; while it waits, other workers claim other indexes. Because JavaScript is single-threaded between awaits, claiming the index is race-free. Writing to results[idx] keeps input order. If fn rejects, Promise.all rejects at once, but the other workers carry on with the remaining items. Add a shared "stop" flag or an AbortSignal if that matters.

    Why the other options are wrong

    • A. JavaScript runs one piece of code at a time; const idx = i++ can't be interleaved.

    • B. Each result is stored at its own index, so input order is kept.

    • D. Array.from({ length: limit }, ...) calls worker exactly limit times.

  7. Question 7Types, Values and Objects

    What does this log?

    const n = 5;
    console.log('n is ' + n > 3 ? 'big' : 'small', false ? 1 : true ? 2 : 3);
    • A

      n is big 2

    • B

      small 2

    • C

      big 3

    • D

      n is small 1

    Show the answer and explanation

    Answer: B

    Operator precedence makes the first argument (("n is " + n) > 3) ? "big" : "small". "n is 5" converts to NaN, and any comparison with NaN is false, so it prints small. The conditional operator is right-associative, so false ? 1 : true ? 2 : 3 means false ? 1 : (true ? 2 : 3), which is 2. Wrap the ternary in parentheses when it sits inside a larger expression.

    Why the other options are wrong

    • A. + binds tighter than ?:, so the string is built first and then compared.

    • C. The comparison is false, and false ? 1 : (true ? 2 : 3) evaluates to 2.

    • D. The whole concatenation becomes the condition, so its text is never printed.

  8. Question 8Functions and Data

    Which statements about rest are true after this code runs? (Choose two.)

    const base = { a: 1, b: 2 };
    const obj = Object.create(base);
    obj.c = 3;
    Object.defineProperty(obj, 'd', { value: 4, enumerable: false });
    const { c, ...rest } = obj;

    Choose 2.

    • A

      rest has no a or b, because rest copies only own properties

    • B

      rest has no d, because rest skips non-enumerable properties

    • C

      rest is { a: 1, b: 2, d: 4 }

    • D

      rest keeps base as its prototype

    • E

      The destructuring throws, because d is not writable

    Show the answer and explanation

    Answer: A and B

    Object rest (and object spread) copy own, enumerable properties into a new plain object. Here the only own enumerable properties are c (taken out by name) and nothing else, so rest is {}. The same rules explain why spreading a class instance loses its methods: they live on the prototype.

    Why the other options are wrong

    • C. It holds neither the inherited keys nor the non-enumerable one.

    • D. Rest creates a fresh plain object whose prototype is Object.prototype.

    • E. Reading a property doesn't need it to be writable, and d isn't copied anyway.

  9. Question 9Browser and Interview Coding

    The page has four elements with class item. What happens when this runs?

    const items = document.getElementsByClassName('item');
    for (let i = 0; i < items.length; i++) {
      items[i].classList.remove('item');
    }
    • A

      All four elements lose the class, because the loop visits each index once

    • B

      Only the first and third elements lose the class

    • C

      Only the first element loses the class, and then the loop ends

    • D

      It throws, because a collection cannot change while it is being looped over

    Show the answer and explanation

    Answer: B

    getElementsByClassName returns a live HTMLCollection. i=0 removes item 1, leaving [2,3,4] with length 3. i=1 now points at item 3 and removes it, leaving [2,4] with length 2. i=2 ends the loop. Use querySelectorAll (a static NodeList), loop backwards, or copy with Array.from first.

    Why the other options are wrong

    • A. The collection shrinks as you go, so indexes shift under the loop.

    • C. The loop continues while i is below the shrinking length, which takes two iterations.

    • D. Live collections update silently; there is no concurrent-modification error.

  10. Question 10Modern Language and the Engine

    Which two statements about symbols are true? (Choose two.)

    Choose 2.

    • A

      Symbol.for('app') returns the same symbol every time it is called with 'app'

    • B

      Symbol.keyFor(Symbol('app')) returns 'app'

    • C

      new Symbol('x') creates a symbol wrapped in an object

    • D

      'id: ' + Symbol('x') throws a TypeError

    • E

      Object.keys() lists symbol-keyed properties after the string keys

    Show the answer and explanation

    Answer: A and D

    Symbol() always makes a unique value; Symbol.for(key) shares one through a global registry, and Symbol.keyFor maps it back. Symbols can't be created with new and can't be implicitly converted to strings, which prevents accidental concatenation. Symbol-keyed properties are skipped by Object.keys, for...in and JSON.stringify.

    Why the other options are wrong

    • B. keyFor only knows registry symbols; for a plain Symbol() it returns undefined.

    • C. Symbol is not a constructor; calling it with new throws a TypeError.

    • E. Object.keys returns only string keys; use Object.getOwnPropertySymbols or Reflect.ownKeys.

Practise all 498 JS questions

Start with the free 15-question diagnostic. It shows where to focus, and your results carry over if you sign up.

Go to JS