Back to Developer

JavaScript

Core JavaScript for testers and developers — language fundamentals, async, and OOP.

Fundamentals

const vs let vs var: which do you use and why?
Use const by default (the binding cannot be reassigned), let when you need to reassign, and avoid var — it is function-scoped and hoisted with confusing behavior, while const/let are block-scoped. Note const prevents reassignment, not mutation: a const object's properties can still change.
What do map, filter and reduce do, and how do they differ from forEach?
map transforms each element and returns a new array of the same length; filter returns a new array with only the elements passing a predicate; reduce accumulates the array into a single value. forEach just iterates for side effects and returns nothing. map/filter/reduce are pure (don't mutate the original); push/pop/splice/sort do mutate.
What are destructuring and the spread operator used for?
Destructuring unpacks values from arrays or objects into variables: const { name, age } = user, or in function params function f({ id }). Spread (...) copies/merges: const merged = { ...base, env: 'prod' } or const all = [...a, ...b]. Spread copies are shallow — nested objects are shared, not cloned.
What do optional chaining (?.) and nullish coalescing (??) do?
Optional chaining short-circuits to undefined instead of throwing when an intermediate value is null/undefined: res.data?.user?.name. Nullish coalescing returns the right side only when the left is null or undefined (not for 0 or '', unlike ||): const total = res.data?.order?.total ?? 0. Both are common when asserting on API response shapes.

Async & OOP

What is a Promise and what states can it have?
A Promise represents the eventual result of an async operation. It is pending while in progress, fulfilled on success (handled with .then), or rejected on failure (handled with .catch); .finally always runs. Promise.all runs many in parallel and rejects if any fails, Promise.allSettled waits for all regardless of outcome, and Promise.race resolves with the first to settle.
How does async/await relate to Promises, and sequential vs parallel awaits?
async/await is syntactic sugar over Promises: an async function always returns a Promise, and await pauses inside it until the Promise settles, with errors handled by try/catch. Awaiting in sequence (await a; await b) is slow; for independent work run them in parallel with await Promise.all([a, b]).
What do you need to know about JavaScript classes?
A class has a constructor that sets instance fields with this, instance methods, and static members accessed on the class itself (MathUtils.PI), not on instances. Getters/setters expose computed or guarded properties (get fahrenheit(), set celsius(v) with validation). Static factory methods like ApiResponse.ok(body) are a clean way to build common instances.
How do inheritance and polymorphism work in JavaScript?
A subclass uses extends and must call super(...) in its constructor before using this; it can override methods and call super.method() to extend behavior. Polymorphism means the same call behaves differently per class — e.g. a list of Vehicle/Car/Motorbike each implementing info(). instanceof checks the prototype chain.
How do you encapsulate state in JavaScript?
Use private class fields prefixed with # (this.#balance) so they cannot be read or written from outside; expose controlled access through getters and methods that validate input. The classic pre-# approach is a closure (a factory function with a private variable). Returning a copy (e.g. [...this.#history]) avoids leaking internal references.
Which design patterns show up often in test frameworks?
Singleton (one shared instance, e.g. a Config), Factory (build configured objects, e.g. an HttpClient per environment), Builder (fluent step-by-step construction, e.g. a RequestBuilder), and Observer/EventEmitter (subscribe to events like a test runner emitting pass/fail). Dependency injection — passing collaborators in rather than hard-coding them — makes all of these easy to mock.