Certified Associate JavaScript Programmer study materials
Certified Associate JavaScript Programmer study resources

Certified Associate JavaScript Programmer common mistakes

Certified Associate JavaScript Programmer · Understand the traps and how to avoid them.

Mistake 1

Treating `==` and `===` as interchangeable when comparing a value to `null`/`undefined`, or assuming `==` never type-converts.

Candidates learn 'always use ===' as a slogan, so they cannot predict what 0 == '', null == undefined, or [] == false evaluate to, and they miss the one idiom where == is intentional (x == null matches both null and undefined).

What to do instead

Memorize the coercion rules: equality with null/undefined only matches each other; numeric coercion applies when one side is a number, boolean, or string; objects convert via ToPrimitive. Drill a table of == results against === results until you can derive each outcome rather than recall it.

Mistake 2

Predicting `this` from where a function is defined rather than how it is called, and misreading arrow functions as having their own `this`.

Most candidates come from class-based languages where this is lexically bound to the enclosing object. JavaScript binds this at call time (default, implicit/method, explicit via call/apply/bind, and new), and arrow functions capture the enclosing scope's this permanently.

What to do instead

For any snippet, locate the call site first: obj.method() sets this to obj; a bare fn() in non-strict mode sets it to the global object (undefined in strict mode); new fn() sets it to the fresh instance. Remember arrow functions ignore call-site binding and cannot be used with new.

Mistake 3

Assuming `var` and `let`/`const` behave the same in loops and blocks, and getting closure results wrong in `for` loops.

Candidates know hoisting exists but not its mechanics: var is function-scoped and hoisted with value undefined, while let/const are block-scoped and sit in a temporal dead zone. A for (var i...) loop shares one binding across callbacks; for (let i...) creates a fresh binding per iteration.

What to do instead

Trace declarations separately from assignments. For loops, ask whether the loop variable is var (one shared binding, so callbacks all see the final value) or let (per-iteration binding, so callbacks see each value). Also practice predicting ReferenceError from temporal dead zone access before declaration.

Mistake 4

Confusing `typeof null`, `typeof` of functions and arrays, and `instanceof` versus `Array.isArray` in type-checking questions.

The typeof operator has historical quirks: typeof null is 'object', typeof [] is 'object', and typeof function(){} is 'function'. Candidates also assume instanceof works across realms and for primitives.

What to do instead

Memorize the full typeof result table including null and functions. Use Array.isArray for arrays, x === null for null, and instanceof only for objects from the same realm. Practice explaining why [] instanceof Array is true but a cross-frame array would fail.

Mistake 5

Misjudging asynchronous ordering by assuming `setTimeout(fn, 0)` runs before promise callbacks, or that `await` blocks the whole thread.

Candidates conflate the task queue with the microtask queue. Promise reactions and await continuations are microtasks and drain before the next macrotask, so a zero-delay timer still runs after them. await only pauses the async function, not the program.

What to do instead

For each snippet, classify every callback as synchronous, microtask (promise .then, await continuation, queueMicrotask), or macrotask (timers, I/O, events), then order them: sync first, then all microtasks, then one macrotask at a time. Write out the predicted log order before checking.

Mistake 6

Describing the prototype chain as class inheritance and expecting `constructor` or `instanceof` to reflect the nearest prototype rather than the chain.

Candidates map JavaScript onto class-based mental models. Property lookup walks the prototype chain, instanceof checks the whole chain, and constructor is just an inherited property that can be reassigned or lost when replacing prototype.

What to do instead

Draw the chain for the snippet: instance, then Constructor.prototype, then Object.prototype, then null. Check whether the code replaced prototype wholesale (breaking constructor) or mutated it. Verify instanceof against the full chain, not the immediate prototype.

Mistake 7

Treating arrays and objects as copied on assignment, and expecting `const` to make object contents immutable.

Candidates transfer value-semantics intuition from primitives. Objects and arrays are assigned by reference, and const only prevents rebinding the identifier, not mutation of the referenced object.

What to do instead

Classify every assignment as primitive (copied) or object (reference shared). For const, ask whether the code reassigns the variable (error) or mutates the object (allowed). Learn shallow copy via spread/Object.assign versus deep copy, and note that Object.freeze is shallow.

Mistake 8

Confusing method behaviour on mutation versus return values, e.g. expecting `push` to return the array, `map` to mutate, or `sort` to return a new array.

Candidates memorize method names without their contracts. push/unshift/splice/sort/reverse mutate and return lengths, elements, or the same array; map/filter/slice/concat return new arrays; forEach always returns undefined.

What to do instead

Build a two-column table: mutating methods and their return values versus non-mutating methods. For each snippet, ask what the method returns separately from what it changes. Note that sort compares as strings by default unless a comparator is given.

Mistake 9

Reading `try/catch/finally` as swallowing all errors or assuming `catch` receives the thrown value with a stack, and mishandling `finally` return values.

Candidates overlook that only synchronous throws inside try are caught, that a return in finally overrides earlier returns, and that rethrowing or wrapping changes the error identity. They also assume async rejections are caught without await.

What to do instead

Trace control flow through try, catch, and finally explicitly, including the returned value when finally returns. Remember a rejected promise needs await inside try or a .catch. Practice distinguishing throw new Error('x') from throw 'x' in what catch receives.

Mistake 10

Skipping strict-mode and coercion edge cases such as implicit globals, octal-like literals, `delete` on variables, or `NaN` comparisons.

Candidates treat strict mode as a style preference. It changes runtime behaviour: undeclared assignment throws, this in plain calls is undefined, duplicate parameters are errors, and delete on identifiers is a syntax error. NaN never equals itself, so x === NaN is always false.

What to do instead

Learn the strict-mode deltas as a checklist and apply them to every snippet that has 'use strict'. Use Number.isNaN or Object.is for NaN checks, and note that Object.is also distinguishes +0 from -0. Practice predicting which lines throw versus silently fail.