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.