December Code

ES6 and modern JavaScript interview questions, with answers

ES6, the 2015 edition of JavaScript, and the yearly editions after it changed how everyday code is written, and interviewers check that your JavaScript is current. The questions are practical: when to reach for destructuring, spread or rest, what ?? does that || does not, how optional chaining fails safely, how a Map differs from a plain object, and what arrow functions give up in exchange for being short. Every output shown was produced by running the code in Node.js.

The questions start with the syntax used in almost every modern file, then the newer data structures, modules and generators. Then take the free JavaScript diagnostic — ten questions across every JavaScript topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.What is the difference between the spread and rest operators in JavaScript?

    In short: They share the ... syntax but work in opposite directions: spread expands an array or object into separate elements or properties, and rest gathers the remaining ones into a single array or object.

    Where the dots appear decides which it is. In a function call, an array literal or an object literal, ... spreads: Math.max(...nums), [...a, ...b], or { ...defaults, ...options }, where later properties override earlier ones, as v does below. In a parameter list or a destructuring pattern, ... collects: a parameter written ...rest receives the remaining arguments as a real array, and const { id, ...others } = user separates one property from the rest. A rest element must come last in its pattern. Rest parameters replaced the old arguments object, which was only array-like and does not exist in arrow functions.

    const b = { lang: "js", v: 1 };
    const cfg = { ...b, v: 2 };
    const { lang, ...rest } = cfg;
    console.log(lang, rest.v);
    // js 2
    const max = (...n) =>
      Math.max(...n);
    console.log(max(3, 9, 4));
    // 9
  2. 2.How does destructuring work in JavaScript, and when do its default values apply?

    In short: Destructuring unpacks array elements by position and object properties by name into variables, and a default applies only when the value is undefined — not when it is null.

    const [first, , third] = arr skips an element; const { name: title = "guest" } = user renames a property and gives it a default; nested patterns reach deeper, as in const { address: { city } } = user, which throws a TypeError if address is missing, since there is nothing to take apart. A default replaces only undefined, so an explicit null passes straight through, as this page's sample shows. Swapping two variables without a temporary is [a, b] = [b, a]. The most common everyday use is in function parameters: the connect function below takes named, optional arguments with defaults, and the trailing = {} lets it be called with no argument at all.

    function connect({
      host = "localhost",
      port = 80,
    } = {}) {
      return `${host}:${port}`;
    }
    console.log(connect());
    // localhost:80
    const c = connect({ port: 5 });
    console.log(c);
    // localhost:5
    let a = 1, b = 2;
    [a, b] = [b, a];
    console.log(a, b);
    // 2 1
  3. 3.What do the ?? and ?. operators do in JavaScript?

    In short: a ?? b gives b only when a is null or undefined, unlike ||, which also replaces 0, "" and false; a?.b returns undefined instead of throwing when a is null or undefined.

    Nullish coalescing fixes the fallback idiom: || treats every falsy value as missing, so a legitimate empty title or zero would be replaced by the default, as the first result below shows. ?? replaces only null and undefined. Optional chaining short-circuits: user?.address?.city stops at the first null or undefined and gives undefined, and the forms obj?.[key] and fn?.() cover computed properties and functions that may not exist. The two combine naturally, as in user?.name ?? "anonymous". Two cautions: optional chaining can hide a bug when a value should never be missing, and ?? cannot be mixed with || or && without parentheses, a SyntaxError that forces the intent to be explicit. ??= assigns only when the variable is nullish.

    const title = "";
    const a = title || "Untitled";
    const b = title ?? "Untitled";
    console.log(`[${a}] [${b}]`);
    // [Untitled] []
    const u = { profile: null };
    console.log(u.profile?.city);
    // undefined
  4. 4.What are template literals and tagged templates in JavaScript?

    In short: Template literals are strings in backticks that can span lines and embed expressions with ${}; a tag function placed before one receives the string pieces and the embedded values separately.

    A template literal evaluates each ${} expression and converts it to a string, which reads better than chains of +, and line breaks inside the backticks are kept, so multi-line text needs no escape sequences. A tagged template, written tag followed by a template literal, calls tag with an array of the literal pieces and then each embedded value as a further argument, and whatever the tag returns becomes the result, which need not be a string: below, two values produce three pieces. That is how libraries escape HTML safely, build SQL with parameters instead of concatenation, or style components. String.raw is a built-in tag that leaves backslashes uninterpreted, handy for regular expressions and Windows paths.

    function show(parts, ...vals) {
      return parts.length + "/" +
        vals.length;
    }
    const n = 3;
    console.log(show`a${n}b${n}`);
    // 3/2
    const p = String.raw`C:\new`;
    console.log(p);
    // C:\new
  5. 5.What is the difference between a Map and a plain object in JavaScript?

    In short: A Map accepts keys of any type, keeps insertion order, reports its size directly and has no inherited keys; an object's keys are strings or symbols, and it doubles as a record with methods.

    Use an object for a fixed set of named fields, a record, and a Map for a dictionary whose keys come from data. A Map's keys can be objects, numbers or anything else, while an object converts every key to a string, so obj[1] and obj["1"] are the same entry, as below. A Map iterates in insertion order, has a size property, and is built for frequent additions and deletions; an object has inherited properties such as toString to watch for. Objects serialise with JSON.stringify and a Map does not without converting it, for example through Object.fromEntries. A WeakMap holds object keys weakly, so an entry disappears once its key object is garbage collected, which suits caches and per-object private data.

    const m = new Map();
    const k = { id: 7 };
    m.set(k, "obj key");
    m.set(1, "num");
    console.log(m.get(k), m.size);
    // obj key 2
    const o = {};
    o[1] = "a";
    console.log(o["1"]);
    // a
  6. 6.How do ES modules differ from CommonJS?

    In short: ES modules use import and export, are linked statically before any code runs and are always strict; CommonJS uses require and module.exports, evaluated at run time, and is Node's older system.

    Because import and export must appear at the top level with fixed names, tools can see the whole dependency graph without running any code, which enables tree shaking: unused exports are dropped from a bundle. Imported names are live, read-only bindings, so importers see a module's later updates to an exported let, while a value destructured from require is a copy taken at that moment. ES modules support top-level await, and import() loads a module on demand and returns a promise. In a module, this at the top level is undefined, and each module has its own scope rather than sharing the global one. Node treats .mjs files, and .js files under a package.json whose type is module, as ES modules.

  7. 7.What are generators and iterators in JavaScript?

    In short: An iterator is an object with a next() method that returns { value, done }; a generator function, written function*, produces one, pausing at each yield until the next value is asked for.

    Anything with a Symbol.iterator method that returns an iterator is iterable, which is what for...of, spread and destructuring consume: arrays, strings, Maps, Sets and generator objects all qualify. A generator makes writing one easy. Calling it runs nothing; each next() resumes the body until the next yield, and return ends it. Because values are produced on demand, a generator can describe an infinite sequence, such as the ids below, and the consumer takes only what it needs, as the destructuring does with two values. Generators also showed the pause-and-resume model that async functions now use for await.

    function* ids(start) {
      let n = start;
      while (true) yield n++;
    }
    const gen = ids(100);
    gen.next();
    console.log(gen.next().value);
    // 101
    const [a, b] = ids(1);
    console.log(a + b);
    // 3
  8. 8.What is the difference between arrow functions and regular functions in JavaScript?

    In short: Arrow functions are a shorter syntax with no this, arguments, super or prototype of their own, so they cannot be constructors; regular functions get all of these for each call.

    An arrow with an expression body returns that expression implicitly; with braces, the body needs an explicit return, and returning an object literal needs parentheses, or the braces are read as a block, which is why bad below returns undefined. Because an arrow takes this and arguments from the surrounding scope, it is the right choice for callbacks inside methods and the wrong one for object methods and constructors: calling an arrow with new throws a TypeError. Rest parameters replace arguments. Regular functions are hoisted when written as declarations, while an arrow assigned to a const cannot be called before its line. Neither kind is faster in any way that matters.

    const mk = () => ({ id: 1 });
    const bad = () => { id: 1 };
    console.log(mk().id, bad());
    // 1 undefined

How the diagnostic asks it

One question from the JavaScript bank, exactly as a sitting would show it. The bank has 4 on modern javascript (es6+) and 30 across JavaScript.

Modern JavaScript (ES6+) · mediumJS-028

What does this code print?

const { a = 1, b = 2 } = { a: undefined, b: null };
console.log(a, b);
  1. 11 nullcorrect
  2. 21 2
  3. 3undefined null
  4. 4undefined 2

A destructuring default is used only when the value is undefined. a is undefined, so it takes the default 1; b is null, which is a real value, so the default is not used and b stays null. 1 2 assumes null also triggers the default. undefined null ignores defaults altogether. undefined 2 reverses the rule. Default parameters in functions follow the same undefined-only rule.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 JavaScript questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated