Felt easy until I second-guessed myself on empty string vs zero vs null vs undefined.
Start by clearly categorizing JavaScript's data types into primitives and objects, then explain the concept of falsy values with concrete examples. Emphasize that understanding these fundamentals is crucial for writing robust code and avoiding common pitfalls.
Pro tip: Mention that falsy values are a common source of bugs in conditionals and that using strict equality (===) or explicit checks can prevent unexpected behavior. Also, note that while NaN is falsy, it is not equal to itself, which is a subtle but important detail.
List JavaScript's primitive data types (string, number, bigint, boolean, undefined, symbol, null) and the object type. Clarify that objects include arrays, functions, and dates.
Enumerate the eight falsy values: false, 0, -0, 0n, '', null, undefined, and NaN. Mention that all other values are truthy, including empty arrays and objects.
Give code examples showing how these values behave in boolean contexts, such as if statements or logical operators. Highlight common pitfalls like using 'if (x)' when x could be 0 or ''.
Explain why this matters in real-world coding, such as avoiding bugs in conditionals and understanding type coercion. Mention best practices like using strict equality or explicit checks.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Start by explaining that then, catch, and finally each return a new Promise, enabling chaining. Then describe error propagation: errors thrown in the executor reject the promise, errors in handlers reject the returned promise, and async functions return rejected promises. Emphasize that finally passes through the original value or error unless it throws.
Pro tip: Mention that unhandled rejections can crash Node.js processes and that attaching a catch at the end of a chain is crucial for robustness. Also, note that finally does not receive the value or error, so it cannot alter the outcome unless it throws.
State that each method returns a new Promise, which resolves with the handler's return value or rejects if the handler throws.
If the executor function throws synchronously, the promise is rejected with that error.
If a then or catch handler throws, the promise returned by that handler is rejected with the thrown error.
An async function always returns a Promise; if it throws, the returned promise is rejected with the thrown error.
finally does not receive the value or error and passes through the original resolution or rejection unless it throws or returns a rejected promise.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Talked through key types first since that's the clearest difference, then iteration order.
Structure your answer by comparing plain objects and Map across the four specified dimensions: key types, iteration order, prototype properties, and performance. For each dimension, state the key differences clearly and provide concrete examples or use cases. Conclude with a brief summary of when to use each data structure.
Pro tip: Emphasize that Map is optimized for frequent additions and removals and preserves insertion order, making it ideal for scenarios requiring ordered iteration or non-string keys. Mention that plain objects are more suitable for simple data storage with string keys and when JSON serialization is needed.
Explain that plain objects only allow string and symbol keys, while Map allows any value as a key, including objects and functions.
Describe that Map maintains insertion order for iteration, whereas plain objects have complex ordering rules (integer-like keys first, then strings in insertion order, then symbols).
Highlight that plain objects inherit properties from Object.prototype, which can cause conflicts, while Map does not have this issue as it doesn't have a prototype chain for keys.
Discuss that Map is generally more performant for frequent additions and deletions, while plain objects may be faster for simple lookups and are more memory-efficient for small, static data.
Summarize when to use each: Map for dynamic collections with non-string keys or frequent updates, plain objects for simple data storage and JSON compatibility.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Start by defining WeakMap as a collection of key-value pairs where keys must be objects and are weakly referenced, then contrast with Map which allows any key type and holds strong references. Explain how weak references enable garbage collection of keys when no other references exist, and discuss the implications for iteration and memory management.
Pro tip: Mention that WeakMap is not enumerable and has no size property, which prevents memory leaks but limits its use to private data or metadata storage. Also, note that WeakMap keys are not enumerable, so you can't list them, which is a key trade-off.
Provide a clear definition of both data structures, emphasizing that WeakMap holds weak references to keys while Map holds strong references.
Explain that WeakMap keys must be objects (or non-registered symbols in some environments), whereas Map keys can be any type, including primitives.
Describe how WeakMap allows keys to be garbage collected when no other references exist, preventing memory leaks, while Map keeps keys alive as long as the Map exists.
Highlight that WeakMap is not iterable, has no size property, and lacks methods like keys(), values(), entries(), and forEach(), making it suitable for private data or caching, while Map is fully enumerable and general-purpose.
Conclude by summarizing the trade-offs: WeakMap offers memory efficiency and privacy but limited functionality, while Map provides full control and enumeration at the cost of potential memory retention.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Probably the best question of the session.
Start by clarifying that WebSockets run over TCP, which guarantees in-order and reliable delivery per connection, but ordering can break across reconnections, multiple connections, or application-level retries. Then discuss scenarios where ordering breaks down and propose a design using sequence numbers, acknowledgments, and idempotent message handling to ensure correctness.
Pro tip: Emphasize that ordering and idempotency are application-level concerns, not transport-level guarantees, and that designing for them requires explicit mechanisms like sequence numbers and deduplication keys. This shows you understand the difference between transport and application semantics.
Explain that WebSockets use TCP, which ensures reliable, in-order delivery of bytes within a single connection. However, message boundaries are not preserved, and ordering is only guaranteed for data sent on that connection.
Discuss when ordering can break: reconnections (new TCP connection may have different latency), multiple connections from the same client, load balancing across servers, or application-level retries that duplicate or reorder messages.
Propose using monotonically increasing sequence numbers per message stream, with the receiver buffering out-of-order messages and delivering them in order. Alternatively, use a central sequencer or a message queue that preserves order.
Suggest attaching unique message IDs (e.g., UUIDs) and having the receiver deduplicate based on these IDs. For state-changing operations, use idempotent APIs or store processed IDs to avoid double-processing.
Discuss trade-offs: buffering increases memory and latency, deduplication requires storage, and strict ordering may limit scalability. Mention handling of message loss, reconnection logic, and the CAP theorem implications.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.