DavidGadyan

JavaScript: Syntax Cheatsheet and Practice Quiz

Twelve syntax patterns behind most JavaScript assessment questions: arrays, objects, closures, promises and the event loop, with a 20-question practice quiz.

Syntax cheatsheet

12 patterns. Pick one on the map for its syntax, when to use it and the trap behind it.

  1. Basics

    const, let and var

    Scope and reassignment

    Syntax
    const x = 10;   // no reassignment (an object inside can still change)
    let y = 20;     // block-scoped, reassignable
    var z = 30;     // function-scoped, hoisted as undefined: avoid
    When to use it
    Default to const; switch to let only when the binding must be reassigned (counters, loop indexes). Never var in new code.
    What it does
    const and let are block-scoped and sit in the temporal dead zone until their line runs. var is function-scoped and hoisted to the top of the function with the value undefined. Function declarations hoist whole, so they can be called before their line.
    Watch out
    const freezes the binding, not the value: const user = {} still allows user.name = "x". Reading a let before its declaration throws a ReferenceError; reading a var gives undefined.
    Example
    console.log(a); // undefined (var is hoisted) var a = 1; console.log(b); // ReferenceError (temporal dead zone) let b = 2; hello(); // works: function declarations hoist whole function hello() {}
  2. Basics

    === and truthy / falsy

    Strict comparison

    Syntax
    5 === "5"   // false  (value AND type)
    5 == "5"    // true   (coerces: avoid)
    // falsy: false, 0, -0, "", null, undefined, NaN
    // truthy: everything else, including [], {}, "0", "false"
    When to use it
    Every comparison. The one accepted use of == is x == null, which matches both null and undefined.
    What it does
    === compares value and type without coercion. Boolean contexts (if, &&, ||, !) convert the value using the falsy list: seven falsy values, everything else is truthy.
    Watch out
    An empty array and an empty object are truthy, so if (arr) does not test emptiness: use arr.length === 0. NaN !== NaN, so use Number.isNaN(). [] == false is true because of coercion.
    Example
    [] == false // true (coercion) [] === false // false if (arr.length === 0) {} // the right emptiness check Number.isNaN(NaN) // true null == undefined // true; null === undefined // false
  3. Arrays

    map, filter, reduce

    Transform, keep, fold

    Syntax
    nums.map(x => x * 2)                 // same length, new values
    nums.filter(x => x % 2 === 0)        // a subset
    nums.reduce((acc, x) => acc + x, 0)  // one value
    When to use it
    map to transform every element, filter to keep a subset, reduce to produce one value: a sum, a lookup object, grouped data.
    What it does
    All three return a new array or value and leave the original untouched, so they chain top to bottom like a pipeline. The callback receives (element, index, array).
    Watch out
    Always pass reduce an initial value: without it the first element becomes the accumulator and an empty array throws. A map callback with braces and no return gives an array of undefined. forEach returns undefined, so it cannot be chained.
    Example
    const total = orders .filter(o => o.status === 'completed') .map(o => o.amount) .reduce((sum, a) => sum + a, 0); const byId = users.reduce((acc, u) => { acc[u.id] = u; return acc; }, {});
  4. Arrays

    find, some, every, sort

    Search and order

    Syntax
    users.find(u => u.id === 5)      // first match or undefined
    users.some(u => u.age >= 18)     // at least one → boolean
    users.every(u => u.age >= 18)    // all → boolean
    [1, 2, 3].includes(2)            // true
    nums.sort((a, b) => a - b)       // ascending numbers (in place)
    When to use it
    find for one item, some and every for yes/no checks, includes for primitives, sort with a comparator for anything numeric or by field.
    What it does
    find and some stop at the first hit; every stops at the first miss. sort reorders the array in place using the sign of the comparator: negative keeps a before b.
    Watch out
    sort() without a comparator sorts as strings (10 before 9) and it mutates: copy first with [...nums].sort() or use toSorted(). findIndex returns -1 when nothing matches, find returns undefined.
    Example
    const sorted = [...users].sort((a, b) => a.age - b.age); // original untouched const desc = [...nums].sort((a, b) => b - a); users.sort((a, b) => a.name.localeCompare(b.name)); [10, 9, 1].sort() // [1, 10, 9] (string order!)
  5. Objects

    Spread and destructuring

    Copy, merge, unpack

    Syntax
    const b = [...a, 3, 4];                 // array copy + append
    const updated = { ...user, age: 34 };   // object copy + override
    const { name, age = 0 } = user;         // unpack with a default
    const [first, , third] = list;          // skip a position
    const { id, ...rest } = obj;            // rest must be last
    function User({ name, age }) {}         // destructured parameter
    When to use it
    Immutable updates (React state), merging defaults with options, pulling fields out of API responses and props.
    What it does
    Spread copies own enumerable properties or iterable items into a new array or object; later keys override earlier ones. Destructuring binds variables to positions or keys in one line.
    Watch out
    Spread is shallow: nested objects are still shared references. Rest must be the last element. Destructuring a missing key gives undefined unless you add a default.
    Example
    const defaults = { theme: 'light', lang: 'en' }; const opts = { ...defaults, lang: 'es' }; // { theme: 'light', lang: 'es' } const { data: { items = [] } = {} } = response; // nested with defaults const [x, y] = [y, x]; // swap
  6. Objects

    ?. and ??

    Safe access, real defaults

    Syntax
    user?.address?.city        // undefined instead of a TypeError
    user.getName?.()           // call only if it exists
    items?.[0]
    const name = user.name ?? 'Unknown';   // only null / undefined fall back
    count ??= 10;              // assign only when null / undefined
    When to use it
    Reading nested API data that may be missing; defaults for values where 0, "" or false are legitimate.
    What it does
    ?. short-circuits to undefined when the left side is null or undefined. ?? returns the right side only for null or undefined, unlike ||, which also replaces 0, "" and false.
    Watch out
    0 || 10 is 10 but 0 ?? 10 is 0. Optional chaining does not catch errors thrown inside the call, only a missing receiver. Mixing ?? with || or && needs parentheses.
    Example
    const port = config.port ?? 3000; // keeps an explicit 0 const label = config.label || 'n/a'; // '' becomes 'n/a' const city = order?.customer?.address?.city ?? '—';
  7. Objects

    Object.keys, values, entries

    Iterate objects

    Syntax
    Object.keys(obj)       // ['a', 'b']
    Object.values(obj)     // [1, 2]
    Object.entries(obj)    // [['a', 1], ['b', 2]]
    Object.fromEntries(pairs)
    Object.hasOwn(obj, 'a')
    When to use it
    Looping over a lookup object, converting between arrays and objects, filtering or mapping an object's fields.
    What it does
    The three accessors return arrays of own enumerable string keys in insertion order, so every array method becomes available; fromEntries reverses entries.
    Watch out
    Keys are strings even for numeric-looking properties. for...in also walks inherited properties: prefer Object.keys. Use Object.hasOwn instead of obj.hasOwnProperty.
    Example
    const prices = { a: 10, b: 25, c: 7 }; const expensive = Object.fromEntries( Object.entries(prices).filter(([, p]) => p > 9) ); // { a: 10, b: 25 } for (const [k, v] of Object.entries(prices)) console.log(k, v);
  8. Collections

    Map and Set

    Counting and dedupe

    Syntax
    const freq = new Map();
    for (const x of arr) freq.set(x, (freq.get(x) ?? 0) + 1);
    freq.has(x); freq.size; [...freq.entries()]
    
    const unique = [...new Set(arr)];
    When to use it
    Frequency counts, O(1) lookups by any key type, removing duplicates, caching results, detecting duplicates in one pass.
    What it does
    Map keeps insertion order, allows object keys and has O(1) average get, set and has. Set stores unique values with the same operations. Both are iterable with for...of.
    Watch out
    Map.get on a missing key returns undefined, hence the ?? 0. Object keys are compared by reference, not by shape, so Set does not dedupe objects with equal contents. A plain object also works as a counter but turns keys into strings.
    Example
    const count = {}; for (const w of words) count[w] = (count[w] || 0) + 1; const hasDuplicate = new Set(nums).size !== nums.length; const top = [...freq.entries()].sort((a, b) => b[1] - a[1])[0]; // most frequent
  9. Functions

    Closures, this and debounce

    Functions that remember

    Syntax
    function counter() {
      let count = 0;
      return () => ++count;      // remembers count
    }
    const inc = counter(); inc(); inc();   // 2
    
    function debounce(fn, delay) {
      let timer;
      return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); };
    }
    When to use it
    Private state without classes, memoisation, handlers that need context, search boxes (debounce) and scroll or resize handlers (throttle).
    What it does
    An inner function keeps a live reference to its outer scope after the outer function has returned. Arrow functions take this from the surrounding scope; regular functions get this from how they are called.
    Watch out
    Debounce fires once after the user stops; throttle fires at most once per interval. A var in a loop shares one binding across all closures; let creates one per iteration.
    Example
    function throttle(fn, delay) { let last = 0; return (...args) => { const now = Date.now(); if (now - last >= delay) { last = now; fn(...args); } }; } const search = debounce(q => fetch('/api?q=' + q), 300); // one request after typing stops
  10. Async

    Promises and async / await

    Waiting without blocking

    Syntax
    async function getUsers() {
      try {
        const res = await fetch('/api/users');
        if (!res.ok) throw new Error('Request failed');
        return await res.json();
      } catch (err) { console.error(err); }
    }
    const [users, posts] = await Promise.all([getUsers(), getPosts()]);
    When to use it
    Any I/O: HTTP calls, timers, file reads. Promise.all for independent requests, allSettled when some may fail, race for timeouts, any for the first success.
    What it does
    await pauses the async function, not the thread, and unwraps the promise. An async function always returns a promise; a thrown error becomes a rejection that .catch or try/catch receives.
    Watch out
    fetch only rejects on network failure, so check res.ok for 404 and 500. Promise.all rejects as soon as one promise rejects. await inside a for loop runs sequentially; collect promises and Promise.all them to run in parallel.
    Example
    Promise.resolve(1).then(x => x + 1) // resolves to 2 const results = await Promise.allSettled(tasks); const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value); await Promise.race([fetchData(), timeout(5000)]);
  11. Async

    Event loop order

    Sync, microtasks, macrotasks

    Syntax
    console.log('A');
    setTimeout(() => console.log('B'), 0);            // macrotask
    Promise.resolve().then(() => console.log('C'));   // microtask
    console.log('D');
    // A D C B
    When to use it
    Every "what is printed?" question, and every bug where a callback runs later than expected.
    What it does
    The call stack runs synchronous code to the end, then all queued microtasks (promise callbacks, queueMicrotask), then one macrotask (setTimeout, setInterval, I/O), then the microtasks again, and so on.
    Watch out
    setTimeout(fn, 0) is never immediate. An await splits the function: the code after it is a microtask. A long synchronous loop blocks everything, including rendering and timers.
    Example
    async function f() { console.log(1); await null; console.log(3); } f(); console.log(2); // 1 2 3
  12. Patterns

    Two pointers, sliding window, Big-O

    From O(n²) to O(n)

    Syntax
    let l = 0, r = arr.length - 1;            // two pointers
    while (l < r) { /* compare arr[l], arr[r] */ l++; r--; }
    
    let left = 0;                              // sliding window
    for (let right = 0; right < arr.length; right++) {
      while (/* window invalid */) left++;
      // arr[left..right] is a valid window
    }
    When to use it
    Pairs in a sorted array, palindromes, longest substring without repeats, max sum of k elements, duplicates in O(n).
    What it does
    Two pointers move inward or in step over sorted data. A sliding window keeps a valid range and only moves its ends forward. A Set or Map turns a nested-loop search into one pass.
    Watch out
    Array index, push, pop and Map/Set operations are O(1); shift and unshift are O(n); sorting is O(n log n). Use a head index instead of shift() for a queue.
    Example
    function isPalindrome(s) { const t = s.toLowerCase().replace(/[^a-z0-9]/g, ''); let l = 0, r = t.length - 1; while (l < r) { if (t[l++] !== t[r--]) return false; } return true; } const seen = new Set(); const firstDup = nums.find(n => seen.has(n) || (seen.add(n), false));

Pick a pattern on the map to see its syntax, when to use it, what it does and what to watch out for.

Practice quiz

20 multiple-choice questions drawn at random from the 40 in this track, one per screen, graded at the end. Every question points back to a pattern on the map.

Pick an answer, move freely between the questions and finish whenever you like: the score and the explanation of every question appear at the end.

  • 20 questions
  • No time limit
  • Graded at the end

The quiz needs JavaScript. The cheatsheet above works without it.

Practice material for timed coding assessments (HackerRank, Codility, TestGorilla and similar). Behaviour described follows the ECMAScript specification as implemented in current browsers and Node.js.