[{"data":1,"prerenderedAt":580},["ShallowReactive",2],{"topic-javascript-modern":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fjavascript.yml","Core JavaScript interview questions covering fundamentals, functions and closures, objects and prototypes, classes and OOP, and asynchronous programming with the event loop.","yml","javascript",{},"JavaScript",1,"frameworks\u002Fjavascript",0,"_TjxyYWBq7dftU9YakX_WX1Z4wAHq9uEUUW7wXL0y0c",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fjavascript-modern.yml","The ergonomic features that define modern JavaScript — destructuring and spread\u002Frest, optional chaining and nullish coalescing, template literals and tagged templates, and Symbols for unique keys and protocol hooks.",{},"Modern JavaScript (ES6+)",7,"modern","topics\u002Fjavascript-modern","7OY2hfQ7yWvC9ZJ6_podaZledkcVcsfjkOUxh5L34a8",[25,178,313,449],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":11,"path":39,"questions":40,"related":171,"seo":172,"seoDescription":173,"stem":174,"subtopic":175,"topic":19,"topicSlug":21,"updated":176,"__hash__":177},"qa\u002Fjavascript\u002Fmodern\u002Fdestructuring-spread-rest.md","Destructuring Spread Rest",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fjavascript\u002Fmodern\u002Fdestructuring-spread-rest",[41,46,50,54,58,62,66,71,75,79,83,87,91,95,99,103,107,111,115,119,123,127,131,135,139,143,147,151,155,159,163,167],{"id":42,"difficulty":43,"q":44,"a":45},"what-is-destructuring","easy","What is destructuring in JavaScript?","**Destructuring** is syntax that unpacks values from objects (or arrays) into\ndistinct variables in a single expression. It replaces repetitive\nproperty-by-property assignment with a compact pattern that mirrors the shape\nof the data.\n\nThe mechanism: the **pattern** on the left of `=` is matched against the\n**value** on the right, binding each name to the corresponding property.\n\n```js\nconst user = { name: 'Ada', age: 36 }\nconst { name, age } = user           \u002F\u002F two variables in one line\nconsole.log(name, age)               \u002F\u002F 'Ada' 36\n\u002F\u002F verbose old way:\n\u002F\u002F const name = user.name; const age = user.age\n```\n\nPitfall: destructuring **reads** properties — it does not mutate the source\nobject. `user` is untouched after the line above.\n",{"id":47,"difficulty":43,"q":48,"a":49},"object-destructuring-basics","How does object destructuring match variables to properties?","It matches **by property name**, not by position (unlike array destructuring).\nThe variable name must equal the key you want to extract.\n\n```js\nconst point = { x: 1, y: 2 }\nconst { y, x } = point               \u002F\u002F order doesn't matter\nconsole.log(x, y)                    \u002F\u002F 1 2\nconst { z } = point                  \u002F\u002F z is undefined (no such key)\n```\n\nPitfall: because matching is by name, a typo silently produces `undefined`\nrather than an error — there is no \"this key doesn't exist\" warning.\n",{"id":51,"difficulty":43,"q":52,"a":53},"rename-while-destructuring","How do you rename a variable while destructuring an object?","Use the `key: newName` syntax. The left side of the colon is the **property to\nread**; the right side is the **local variable** to bind it to.\n\n```js\nconst res = { status: 200, data: 'ok' }\nconst { status: code, data: body } = res   \u002F\u002F rename both\nconsole.log(code, body)                     \u002F\u002F 200 'ok'\n\u002F\u002F console.log(status)                      \u002F\u002F ReferenceError — status not defined\n```\n\nThe original key name (`status`) is **not** in scope afterward — only the new\nname is. Pitfall: people read `status: code` as \"assign status to code\" but it's\nthe reverse — read `status`, name it `code`.\n",{"id":55,"difficulty":43,"q":56,"a":57},"default-values","How do default values work in object destructuring?","A default after `=` is applied only when the extracted value is **`undefined`**\n(a missing key or an explicit `undefined`). Any other value — including `null`,\n`0`, `''`, or `false` — is kept as-is.\n\n```js\nconst { timeout = 1000, retries = 3 } = { retries: 0 }\nconsole.log(timeout, retries)        \u002F\u002F 1000 0  0 is kept, default not applied\nconst { x = 5 } = { x: null }\nconsole.log(x)                       \u002F\u002F null  default NOT used — null isn't undefined\n```\n\nPitfall: defaults trigger only on `undefined`, so `null` slips through. If you\nneed a fallback for `null` too, combine with `??` after extracting.\n",{"id":59,"difficulty":35,"q":60,"a":61},"rename-and-default","How do you combine renaming and a default value?","Stack the two syntaxes: `key: newName = default`. JavaScript reads `key`,\nbinds it to `newName`, and falls back to `default` if that value is `undefined`.\n\n```js\nconst settings = {}\nconst { mode: displayMode = 'light' } = settings\nconsole.log(displayMode)             \u002F\u002F 'light'  renamed AND defaulted\n```\n\nThe order is fixed: property name, then colon and new name, then equals and\ndefault. Pitfall: writing `mode = 'light': displayMode` is a syntax error — the\ndefault attaches to the renamed binding, after the colon.\n",{"id":63,"difficulty":35,"q":64,"a":65},"nested-destructuring","How do you destructure nested objects?","Nest a pattern wherever a value would be a sub-object. The colon here means\n\"descend into this object\" rather than \"rename\".\n\n```js\nconst user = { name: 'Ada', address: { city: 'London', zip: 'SW1' } }\nconst { address: { city } } = user\nconsole.log(city)                    \u002F\u002F 'London'\n\u002F\u002F console.log(address)              \u002F\u002F ReferenceError — address itself not bound\n```\n\nTwo gotchas: (1) the intermediate name (`address`) is **not** created as a\nvariable — only the leaf (`city`) is. (2) If `address` were `undefined`,\ndestructuring into it throws **`TypeError: Cannot destructure property 'city'\nof undefined`** — add a default: `{ address: { city } = {} }`.\n",{"id":67,"difficulty":68,"q":69,"a":70},"computed-property-destructuring","hard","How do you destructure a property whose key is computed at runtime?","Use **computed key** syntax `[expr]: target`. The bracketed expression is\nevaluated to a string key, and you must supply a binding name after the colon\n(there's no implicit name to infer).\n\n```js\nconst field = 'email'\nconst record = { email: 'a@b.com', name: 'Ada' }\nconst { [field]: value } = record\nconsole.log(value)                   \u002F\u002F 'a@b.com'  dynamic key extracted\n```\n\nPitfall: you **must** name the target (`: value`) — `const { [field] } = record`\nis a syntax error because the engine can't derive a variable name from an\narbitrary expression.\n",{"id":72,"difficulty":35,"q":73,"a":74},"destructuring-params","How do you destructure function parameters?","Put the pattern directly in the parameter list. The caller passes one object and\nthe function pulls out named fields — a common alternative to long positional\nargument lists.\n\n```js\nfunction createUser({ name, role = 'user', active = true }) {\n  return `${name}:${role}:${active}`\n}\ncreateUser({ name: 'Ada', role: 'admin' })   \u002F\u002F 'Ada:admin:true'\n```\n\nThis gives named, order-independent arguments with per-field defaults. Pitfall:\ncalling `createUser()` with **no argument** throws, because you can't destructure\n`undefined`. Guard with a default: `function createUser({ ... } = {})`.\n",{"id":76,"difficulty":35,"q":77,"a":78},"param-default-object","Why give a destructured parameter a default of {}?","Destructuring `undefined` throws a `TypeError`. Defaulting the whole parameter to\n`{}` lets the function be called with **no argument** while still applying each\nfield's individual defaults.\n\n```js\nfunction connect({ host = 'localhost', port = 5432 } = {}) {\n  return `${host}:${port}`\n}\nconnect()                            \u002F\u002F 'localhost:5432' — no throw\n\u002F\u002F without the = {}, connect() would throw TypeError\n```\n\nTwo levels of defaulting are at work: `= {}` guards the missing-object case, and\n`host = ...` \u002F `port = ...` guard missing fields. Pitfall: forgetting the outer\n`= {}` makes the function fragile to no-arg calls.\n",{"id":80,"difficulty":35,"q":81,"a":82},"rest-in-object-destructuring","What does the rest pattern do in object destructuring?","`...rest` collects all **remaining own enumerable** properties not already named\ninto a new object. It's how you extract a few fields and keep the rest grouped.\n\n```js\nconst props = { id: 1, name: 'Ada', role: 'admin' }\nconst { id, ...rest } = props\nconsole.log(id)                      \u002F\u002F 1\nconsole.log(rest)                    \u002F\u002F { name: 'Ada', role: 'admin' }  new object\n```\n\nThe rest object is a **new shallow object**, distinct from the source. Pitfall:\nthe rest element must be **last** — `const { ...rest, id }` is a syntax error.\n",{"id":84,"difficulty":35,"q":85,"a":86},"rest-omit-property","How do you create a copy of an object without a particular property?","Name the property to drop, then capture `...rest`. The named binding is discarded\nand `rest` holds everything else — an immutable \"omit\" pattern.\n\n```js\nconst user = { id: 1, password: 'secret', name: 'Ada' }\nconst { password, ...safe } = user\nconsole.log(safe)                    \u002F\u002F { id: 1, name: 'Ada' }  password removed\n\u002F\u002F user is unchanged — original still has password\n```\n\nThis is purely a copy; the original is untouched. Pitfall: `password` is now an\nunused variable in scope — some linters flag it; an underscore prefix or\n`\u002F\u002F eslint-disable` comment is the usual fix.\n",{"id":88,"difficulty":43,"q":89,"a":90},"object-spread-basics","What does the object spread operator do?","`...obj` inside an object literal copies that object's **own enumerable**\nproperties into the new object. It's the concise way to clone or extend.\n\n```js\nconst base = { a: 1, b: 2 }\nconst copy = { ...base }             \u002F\u002F shallow clone\nconst extended = { ...base, c: 3 }   \u002F\u002F add a property\nconsole.log(extended)                \u002F\u002F { a: 1, b: 2, c: 3 }\n```\n\nSpread only copies **own** enumerable properties — inherited (prototype) and\nnon-enumerable properties are skipped. Pitfall: it's a **shallow** copy, so\nnested objects are shared by reference, not duplicated.\n",{"id":92,"difficulty":43,"q":93,"a":94},"spread-merge","How do you merge two objects with spread?","List both with `...` in one literal. Properties are copied left-to-right, so a\nlater object's values **override** earlier ones for duplicate keys.\n\n```js\nconst defaults = { theme: 'light', size: 'md' }\nconst overrides = { size: 'lg' }\nconst config = { ...defaults, ...overrides }\nconsole.log(config)                  \u002F\u002F { theme: 'light', size: 'lg' }\n```\n\nThe **last write wins** for any shared key. Pitfall: order matters — putting\n`...defaults` last would let defaults clobber your overrides, which is almost\nalways a bug.\n",{"id":96,"difficulty":35,"q":97,"a":98},"spread-override-order","How does property precedence work when spreading and adding keys together?","The object literal is built **left to right**, and each later key with the same\nname overwrites the earlier one. This works whether the key comes from a spread\nor is written literally.\n\n```js\nconst obj = { a: 1 }\nconsole.log({ ...obj, a: 2 })        \u002F\u002F { a: 2 }  literal after spread wins\nconsole.log({ a: 2, ...obj })        \u002F\u002F { a: 1 }  spread after literal wins\n```\n\nPosition, not source type, decides the winner. Pitfall: spreading after your\nexplicit overrides silently undoes them — keep explicit keys after the spreads\nyou want them to win against.\n",{"id":100,"difficulty":68,"q":101,"a":102},"shallow-copy-gotcha","Why is object spread only a shallow copy, and why does that matter?","Spread copies the top-level property **values**. When a value is an object, it\ncopies the **reference**, not a fresh clone — so nested objects are shared\nbetween the original and the copy.\n\n```js\nconst original = { name: 'Ada', meta: { active: true } }\nconst copy = { ...original }\ncopy.meta.active = false\nconsole.log(original.meta.active)    \u002F\u002F false  original mutated too!\ncopy.name = 'Bob'\nconsole.log(original.name)           \u002F\u002F 'Ada'  top-level is independent\n```\n\nTop-level properties are independent; nested ones are not. For a deep copy use\n`structuredClone(original)` (or libraries). Pitfall: mutating nested state on a\n\"copy\" silently corrupts the source — a frequent source of state bugs.\n",{"id":104,"difficulty":35,"q":105,"a":106},"spread-vs-object-assign","What is the difference between spread and Object.assign?","Both do a shallow merge of own enumerable properties, but `Object.assign(target,\n...sources)` **mutates** `target` and returns it, whereas `{ ...a, ...b }`\nalways creates a **new** object.\n\n```js\nconst target = { a: 1 }\nObject.assign(target, { b: 2 })      \u002F\u002F mutates target -> { a: 1, b: 2 }\nconst fresh = { ...target, c: 3 }    \u002F\u002F new object, target untouched\n```\n\nSubtler difference: `Object.assign` **invokes setters** on the target, while\nspread defines plain data properties on a fresh object. Pitfall: passing a real\nobject as `Object.assign`'s first arg unintentionally mutates it — pass `{}`\nfirst (`Object.assign({}, a, b)`) for a non-mutating merge.\n",{"id":108,"difficulty":68,"q":109,"a":110},"spread-with-getters","What happens to getters when you spread an object?","Spread **invokes** each getter and copies its returned value as a plain data\nproperty — the getter itself is not carried over. The copy holds a static\nsnapshot, not a live accessor.\n\n```js\nconst obj = { _n: 1, get n() { return this._n } }\nconst copy = { ...obj }\nobj._n = 99\nconsole.log(copy.n)                  \u002F\u002F 1  frozen snapshot, getter gone\nconsole.log(Object.getOwnPropertyDescriptor(copy, 'n').get)  \u002F\u002F undefined\n```\n\nTo preserve accessors you need `Object.getOwnPropertyDescriptors` +\n`Object.defineProperties`. Pitfall: spreading objects with getters silently\nflattens computed\u002Flazy properties into stale values.\n",{"id":112,"difficulty":43,"q":113,"a":114},"rest-parameters","What are rest parameters in a function?","A **rest parameter** (`...args`) collects all remaining arguments into a real\narray. It's the modern, array-native replacement for the `arguments` object.\n\n```js\nfunction sum(...nums) {\n  return nums.reduce((t, n) => t + n, 0)\n}\nsum(1, 2, 3)                         \u002F\u002F 6 — nums is [1, 2, 3]\n```\n\nUnlike `arguments`, `nums` is a genuine `Array` with `map`\u002F`reduce`\u002Fetc., and it\nworks in arrow functions. Pitfall: the rest parameter must be **last** —\n`function f(...a, b)` is a syntax error.\n",{"id":116,"difficulty":35,"q":117,"a":118},"rest-vs-arguments","Why prefer rest parameters over the arguments object?","`arguments` is an **array-like** object — no array methods, and it doesn't exist\nin arrow functions. Rest parameters give a **real array** and work everywhere.\n\n```js\nfunction oldWay() {\n  const args = Array.prototype.slice.call(arguments)  \u002F\u002F awkward conversion\n  return args.map(x => x * 2)\n}\nconst newWay = (...args) => args.map(x => x * 2)       \u002F\u002F clean, works in arrows\n```\n\nRest also makes the signature self-documenting and can capture just the tail\nafter named params. Pitfall: `arguments` reflects all passed args ignoring\nparameter names, which surprises people refactoring to rest.\n",{"id":120,"difficulty":35,"q":121,"a":122},"named-plus-rest-params","How do you combine named parameters with a rest parameter?","List the fixed parameters first, then `...rest` last to gather everything\nbeyond them. The named params consume the leading arguments; the rest collects\nthe tail.\n\n```js\nfunction log(level, ...messages) {\n  console.log(`[${level}]`, messages.join(' '))\n}\nlog('INFO', 'server', 'started')     \u002F\u002F level='INFO', messages=['server','started']\n```\n\n`rest` is empty (`[]`) if no extra arguments are passed — never `undefined`.\nPitfall: rest captures everything after the named params, so you can't place a\nrequired parameter after it.\n",{"id":124,"difficulty":35,"q":125,"a":126},"spread-call-arguments","How do you pass an array as individual function arguments?","Use spread at the **call site**: `fn(...arr)` expands the array elements into\npositional arguments. This replaces the older `fn.apply(null, arr)`.\n\n```js\nconst nums = [3, 1, 4, 1, 5]\nMath.max(...nums)                    \u002F\u002F 5 — same as Math.max(3,1,4,1,5)\n\u002F\u002F Math.max(nums)                    \u002F\u002F NaN — array isn't a number\n```\n\nYou can mix spread with literals: `fn(0, ...nums, 9)`. Pitfall: spreading a very\nlarge array can exceed the engine's argument-count limit and throw `RangeError`.\n",{"id":128,"difficulty":43,"q":129,"a":130},"swap-variables","How do you swap two variables without a temp variable?","Array destructuring lets you assign both sides simultaneously: the right side is\nevaluated into a temporary array, then unpacked back into the variables.\n\n```js\nlet a = 1, b = 2\n;[a, b] = [b, a]                     \u002F\u002F a=2, b=1, no temp needed\n```\n\nThe leading semicolon guards against the previous line being interpreted as a\nfunction call on `[`. Pitfall: forgetting that semicolon when the prior line has\nno `;` causes `2[a, b]` ASI bugs — a classic gotcha.\n",{"id":132,"difficulty":35,"q":133,"a":134},"destructuring-return-values","How does destructuring help with functions that return objects?","A function can return one object and the caller destructures the fields it cares\nabout — order-independent, self-documenting, and easy to extend without breaking\ncallers.\n\n```js\nfunction parse(input) {\n  return { ok: true, value: input.trim(), length: input.length }\n}\nconst { value, ok } = parse('  hi ') \u002F\u002F pick fields by name, any order\n```\n\nAdding a new field to the returned object never breaks existing callers (unlike\nadding a positional array element). Pitfall: if the function can return `null`\n(e.g. a \"not found\" case), destructuring it throws — guard with `?? {}`.\n",{"id":136,"difficulty":68,"q":137,"a":138},"deep-default-in-destructuring","How do nested destructuring and defaults interact when an intermediate is missing?","Each level of a nested pattern can carry its own default. A default on an\n**intermediate** object protects against that object being `undefined` before you\ndescend into it.\n\n```js\nfunction render({ theme: { color = 'black' } = {} } = {}) {\n  return color\n}\nrender()                             \u002F\u002F 'black' — every level defaulted\nrender({ theme: null })              \u002F\u002F TypeError — null isn't undefined, can't destructure\n```\n\nDefaults fire on `undefined` only, so a `null` intermediate still throws. Pitfall:\ncallers passing explicit `null` for a sub-object bypass your `= {}` guard — defend\nwith `?? {}` if `null` is possible.\n",{"id":140,"difficulty":35,"q":141,"a":142},"spread-vs-rest-difference","What is the difference between spread and rest, since both use the ... syntax?","They are mirror images. **Rest collects** multiple items into one\narray\u002Fobject (appears in a parameter list or destructuring pattern). **Spread\nexpands** one iterable\u002Fobject into multiple items (appears in a call, array, or\nobject literal).\n\n```js\nconst [first, ...rest] = [1, 2, 3]   \u002F\u002F rest: collects -> rest = [2, 3]\nconst merged = [first, ...rest]      \u002F\u002F spread: expands -> [1, 2, 3]\n```\n\nRule of thumb: on the **left of `=`** or in **params** it's rest (gathering); on\nthe **right** or inside a literal\u002Fcall it's spread (scattering). Pitfall: same\n`...` token, opposite directions — context decides which.\n",{"id":144,"difficulty":35,"q":145,"a":146},"spread-iterables","Can spread be used on things other than arrays?","In an **array literal** or function call, spread works on any **iterable** —\nstrings, `Set`, `Map`, `arguments`, generators. In an **object literal**, spread\nworks on any object's own enumerable properties.\n\n```js\n[...'abc']                           \u002F\u002F ['a', 'b', 'c']  string is iterable\n[...new Set([1, 1, 2])]              \u002F\u002F [1, 2]  dedupe trick\n{ ...{ a: 1 } }                      \u002F\u002F { a: 1 }  object spread\n\u002F\u002F [...{ a: 1 }]                     \u002F\u002F TypeError — plain object isn't iterable\n```\n\nPitfall: object spread and array spread are different mechanisms — a plain object\nis **not** iterable, so `[...obj]` throws even though `{ ...obj }` works.\n",{"id":148,"difficulty":35,"q":149,"a":150},"destructuring-skip-and-rename","How can you destructure and immediately rename a property to avoid a name clash?","Renaming during destructuring sidesteps collisions with existing variables or\nreserved-ish names, and lets you give local, meaningful names.\n\n```js\nconst a = 1\nconst data = { a: 99, default: 'x' }\nconst { a: itemId, default: fallback } = data\nconsole.log(itemId, fallback)        \u002F\u002F 99 'x'  no clash with outer `a`\n```\n\nRenaming also lets you read keys that aren't valid identifiers (like `default`)\ninto usable names. Pitfall: forgetting to rename when a key shadows an in-scope\n`const` causes a redeclaration error.\n",{"id":152,"difficulty":68,"q":153,"a":154},"array-of-objects-destructuring","How do you destructure objects inside an array in one pattern?","Combine array and object patterns: the array pattern picks positions, and each\nelement can itself be an object pattern that picks fields.\n\n```js\nconst users = [{ name: 'Ada' }, { name: 'Bob' }]\nconst [{ name: first }, { name: second }] = users\nconsole.log(first, second)           \u002F\u002F 'Ada' 'Bob'\n```\n\nYou can default a missing element to `{}` to avoid throwing: `[{ name } = {}] =\nusers`. Pitfall: if the array is shorter than the pattern, a missing element is\n`undefined` and destructuring into it throws unless you provide an element\ndefault.\n",{"id":156,"difficulty":35,"q":157,"a":158},"spread-immutable-update","How does spread enable immutable state updates?","Spread the old state and override the changed fields in a new object, leaving the\noriginal untouched — the foundation of immutable updates in Redux, React, etc.\n\n```js\nconst state = { count: 0, user: 'Ada' }\nconst next = { ...state, count: state.count + 1 }\nconsole.log(state.count, next.count) \u002F\u002F 0 1  original preserved\n```\n\nThis makes change detection cheap (reference comparison) and supports\ntime-travel\u002Fundo. Pitfall: it's shallow — updating a **nested** slice requires\nspreading each level (`{ ...state, user: { ...state.user, ... } }`), or nested\nmutations leak into the old state.\n",{"id":160,"difficulty":68,"q":161,"a":162},"default-from-other-param","Can a destructured default reference an earlier-destructured value?","Yes. Defaults are evaluated left to right, so a later field's default can use a\nvalue bound earlier in the same pattern.\n\n```js\nfunction area({ width, height = width } = {}) {\n  return width * height\n}\narea({ width: 5 })                   \u002F\u002F 25 — height defaults to width\n```\n\nThe binding must already be in scope (declared earlier in the pattern). Pitfall:\nreferencing a field declared **after** it throws a temporal-dead-zone\n`ReferenceError` — `{ a = b, b }` fails because `b` isn't initialized yet when\n`a`'s default runs.\n",{"id":164,"difficulty":35,"q":165,"a":166},"spread-null-undefined","What happens when you spread null or undefined?","In an **object** literal, spreading `null` or `undefined` is silently ignored —\nno properties added, no error. In an **array** literal or call, spreading them\n**throws** because they aren't iterable.\n\n```js\nconst o = { ...null, ...undefined, a: 1 }  \u002F\u002F { a: 1 } — ignored safely\n\u002F\u002F const arr = [...null]                    \u002F\u002F TypeError: null is not iterable\n```\n\nThis asymmetry is handy: `{ ...(cond && extra) }` conditionally merges (since\n`false`\u002F`null` spread to nothing). Pitfall: the same `cond && extra` trick in an\n**array** spread throws when the condition is falsy.\n",{"id":168,"difficulty":35,"q":169,"a":170},"rest-shallow-copy","Is the rest object from destructuring a deep or shallow copy?","**Shallow.** The rest pattern builds a new object whose nested values are still\nshared references with the source — only the top-level structure is new.\n\n```js\nconst src = { id: 1, meta: { x: 1 } }\nconst { id, ...rest } = src\nrest.meta.x = 99\nconsole.log(src.meta.x)              \u002F\u002F 99  shared nested reference mutated\n```\n\nSame shallow semantics as object spread (they use the same copy mechanism).\nPitfall: treating the rest object as fully isolated and mutating its nested\nmembers corrupts the original.\n",null,{"description":32},"JavaScript destructuring, spread and rest interview questions — object destructuring with renaming and defaults, nested and computed keys, object spread for copying and merging, rest parameters, and the shallow-copy gotcha.","javascript\u002Fmodern\u002Fdestructuring-spread-rest","Destructuring, Spread & Rest","2026-06-18","j-ktPh8WpFhFjct2cigvLtRd0mYHyPvKzyn8tht5YrQ",{"id":179,"title":180,"body":181,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":185,"navigation":38,"order":33,"path":186,"questions":187,"related":171,"seo":308,"seoDescription":309,"stem":310,"subtopic":311,"topic":19,"topicSlug":21,"updated":176,"__hash__":312},"qa\u002Fjavascript\u002Fmodern\u002Foptional-chaining-nullish.md","Optional Chaining Nullish",{"type":29,"value":182,"toc":183},[],{"title":32,"searchDepth":33,"depth":33,"links":184},[],{},"\u002Fjavascript\u002Fmodern\u002Foptional-chaining-nullish",[188,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,296,300,304],{"id":189,"difficulty":43,"q":190,"a":191},"what-is-optional-chaining","What is optional chaining?","**Optional chaining** (`?.`) accesses a nested property only if the value to its\nleft is **not `null` or `undefined`**; otherwise the whole expression\nshort-circuits and returns `undefined` instead of throwing.\n\n```js\nconst user = { profile: { name: 'Ada' } }\nconsole.log(user.profile?.name)      \u002F\u002F 'Ada'\nconsole.log(user.account?.balance)   \u002F\u002F undefined  no throw\n\u002F\u002F console.log(user.account.balance) \u002F\u002F TypeError: Cannot read 'balance' of undefined\n```\n\nIt replaces long `a && a.b && a.b.c` guards. Pitfall: it guards against `null`\u002F\n`undefined` only — it does **not** protect against other errors like calling a\nnon-function.\n",{"id":193,"difficulty":43,"q":194,"a":195},"optional-chaining-properties","How does ?. work on nested property access?","`a?.b` evaluates `a`; if it's `null`\u002F`undefined` it returns `undefined`,\notherwise it accesses `b`. Chaining several `?.` guards each link in the path.\n\n```js\nconst data = { a: { b: null } }\nconsole.log(data?.a?.b?.c)           \u002F\u002F undefined  stops safely at b being null\n```\n\nImportantly, `?.` only checks the value **immediately to its left**. Pitfall:\n`data.a?.b.c` guards `a` but **not** `b` — if `b` is `null`, accessing `.c`\nstill throws. Put `?.` at every uncertain link.\n",{"id":197,"difficulty":35,"q":198,"a":199},"optional-chaining-methods","How do you optionally call a method that might not exist?","Use `obj.method?.()`. If `method` is `null` or `undefined`, the call is skipped\nand the expression yields `undefined`; otherwise the method runs normally.\n\n```js\nconst api = { log: () => 'logged' }\nconsole.log(api.log?.())             \u002F\u002F 'logged'\nconsole.log(api.track?.())           \u002F\u002F undefined  no track method, no throw\n```\n\nHandy for optional callbacks: `onChange?.(value)`. Pitfall: if `method` exists\nbut is **not a function** (e.g. a number), `?.()` still throws `TypeError: not a\nfunction` — `?.` only guards `null`\u002F`undefined`, not wrong types.\n",{"id":201,"difficulty":35,"q":202,"a":203},"optional-chaining-arrays","How does optional chaining work with array\u002Fdynamic access?","Use `arr?.[index]`. The `?.` before the bracket guards the array itself being\n`null`\u002F`undefined` before the element is read.\n\n```js\nconst result = { items: ['a', 'b'] }\nconsole.log(result.items?.[0])       \u002F\u002F 'a'\nconsole.log(result.tags?.[0])        \u002F\u002F undefined  no tags array, no throw\n```\n\nSame applies to computed keys: `obj?.[key]`. Pitfall: `arr?.[0]` guards `arr`,\nnot the element — if `arr` is `[]`, `arr?.[0]` is `undefined` (fine), but\n`arr?.[0].name` throws because element `0` is `undefined`.\n",{"id":205,"difficulty":35,"q":206,"a":207},"short-circuiting","What does short-circuiting mean in optional chaining?","When `?.` hits a `null`\u002F`undefined`, it **stops evaluating the rest of the\nchain** and returns `undefined` immediately — nothing to the right runs,\nincluding function calls.\n\n```js\nlet calls = 0\nconst obj = null\nconst r = obj?.method(calls++)       \u002F\u002F undefined\nconsole.log(calls)                   \u002F\u002F 0  method() and calls++ never ran\n```\n\nThe entire chain after the short-circuit is skipped, side effects included.\nPitfall: people assume arguments still evaluate — they don't, so relying on a\nside effect inside a short-circuited call is a silent bug.\n",{"id":209,"difficulty":43,"q":210,"a":211},"nullish-coalescing","What is the nullish coalescing operator ??","`a ?? b` returns `a` unless it is **`null` or `undefined`**, in which case it\nreturns `b`. It provides a default only for \"no value\", not for any falsy value.\n\n```js\nconst port = userPort ?? 3000        \u002F\u002F use userPort unless it's null\u002Fundefined\nconsole.log(0 ?? 5)                  \u002F\u002F 0  0 is a real value, kept\nconsole.log(null ?? 5)               \u002F\u002F 5  null -> fallback\n```\n\nIt treats `0`, `''`, `false`, and `NaN` as legitimate values. Pitfall: don't\nreach for `??` when you actually want to reject *all* falsy values — that's `||`.\n",{"id":213,"difficulty":35,"q":214,"a":215},"nullish-vs-or","What is the difference between ?? and ||?","`||` falls back on **any falsy** value (`0`, `''`, `false`, `NaN`, `null`,\n`undefined`). `??` falls back only on **`null`\u002F`undefined`**. The difference\nmatters whenever a falsy value is valid.\n\n```js\nconst count = 0\nconsole.log(count || 10)             \u002F\u002F 10  treats valid 0 as \"missing\"\nconsole.log(count ?? 10)             \u002F\u002F 0   keeps the real 0\nconsole.log('' || 'default')         \u002F\u002F 'default'  empty string lost\nconsole.log('' ?? 'default')         \u002F\u002F ''  empty string kept\n```\n\nUse `??` for defaults of numbers\u002Fstrings\u002Fbooleans. Pitfall: blindly migrating\nevery `||` to `??` (or vice-versa) changes behavior for zero\u002Fempty inputs — a\ncommon subtle regression.\n",{"id":217,"difficulty":35,"q":218,"a":219},"when-to-use-or","When is || still the right choice over ???","Use `||` when you genuinely want **any falsy value** to trigger the fallback —\ne.g. treating empty string, `0`, or `false` the same as \"absent.\"\n\n```js\nconst name = input.name || 'Anonymous'   \u002F\u002F '' should also become Anonymous\nconst items = response.list || []        \u002F\u002F falsy\u002Fempty -> safe default array\n```\n\nHere an empty string genuinely means \"no name,\" so `||` is correct and `??`\nwould let `''` through. Pitfall: the choice is semantic — ask \"is a falsy value a\nlegitimate value here?\" If yes use `??`; if no, `||` is fine.\n",{"id":221,"difficulty":35,"q":222,"a":223},"logical-assignment-nullish","What does the ??= operator do?","`a ??= b` assigns `b` to `a` **only if `a` is currently `null` or `undefined`**.\nIt's shorthand for `a = a ?? b`, but with a key twist: it assigns only when\nneeded.\n\n```js\nconst config = { timeout: 0 }\nconfig.timeout ??= 1000              \u002F\u002F stays 0  (0 isn't nullish)\nconfig.retries ??= 3                 \u002F\u002F becomes 3  (was undefined)\nconsole.log(config)                  \u002F\u002F { timeout: 0, retries: 3 }\n```\n\nIt only writes when the left side is nullish, so existing valid values (even\nfalsy ones) survive. Pitfall: confusing it with `||=`, which would overwrite the\n`0`.\n",{"id":225,"difficulty":35,"q":226,"a":227},"logical-assignment-or","What does ||= do and how does it differ from ??=?","`a ||= b` assigns `b` when `a` is **falsy** (`a = a || b`); `a ??= b` assigns\nonly when `a` is **nullish**. The split mirrors the `||` vs `??` distinction.\n\n```js\nlet x = 0\nx ||= 5                              \u002F\u002F x = 5  overwrote valid 0\nlet y = 0\ny ??= 5                              \u002F\u002F y = 0  kept valid 0\n```\n\nBoth **short-circuit**: if no assignment is needed, the right side never\nevaluates (no side effects, no setter triggered). Pitfall: using `||=` to set a\ndefault on a numeric\u002Fboolean field clobbers legitimate `0`\u002F`false`.\n",{"id":229,"difficulty":35,"q":230,"a":231},"logical-assignment-and","What does the &&= operator do?","`a &&= b` assigns `b` to `a` **only if `a` is truthy** (`a = a && b`). It's used\nto update a value only when it already exists.\n\n```js\nlet user = { name: 'Ada' }\nuser.name &&= user.name.toUpperCase()\nconsole.log(user.name)               \u002F\u002F 'ADA'  updated because it was truthy\nlet empty = null\nempty &&= 'x'                        \u002F\u002F stays null  falsy, left alone\n```\n\nLike the others it short-circuits — `b` only evaluates when `a` is truthy.\nPitfall: it's the least common of the three; people often reach for an `if` when\n`&&=` would read more cleanly (or vice versa where `if` is clearer).\n",{"id":233,"difficulty":35,"q":234,"a":235},"combining-optional-and-nullish","How do you combine ?. with ?? for a safe default?","This is the canonical pair: `?.` safely walks a possibly-missing path and yields\n`undefined`, then `??` supplies a default for that `undefined`.\n\n```js\nconst user = { settings: null }\nconst theme = user?.settings?.theme ?? 'light'\nconsole.log(theme)                   \u002F\u002F 'light'  safe walk + default\n```\n\n`?.` handles the \"path might not exist,\" `??` handles the \"so use a fallback.\"\nPitfall: using `||` instead of `??` here would also override a legitimate\n`theme` of `''` or `0`, which `??` correctly preserves.\n",{"id":237,"difficulty":68,"q":238,"a":239},"precedence-with-nullish","Why must you parenthesize ?? when mixing it with || or &&?","JavaScript **forbids** combining `??` with `||` or `&&` without parentheses — it's\na **syntax error** by design, because the intended precedence is ambiguous to\nreaders.\n\n```js\n\u002F\u002F const x = a || b ?? c             \u002F\u002F SyntaxError\nconst x = (a || b) ?? c              \u002F\u002F explicit grouping required\nconst y = a || (b ?? c)              \u002F\u002F the other grouping\n```\n\nThe language forces you to disambiguate rather than guessing. Pitfall: this is a\ncompile-time error, not a runtime one — copying a clever one-liner from `&&`\u002F`||`\nland into a `??` expression won't even parse.\n",{"id":241,"difficulty":68,"q":242,"a":243},"optional-chaining-not-assignment","Can you use optional chaining on the left side of an assignment?","No. `?.` is only valid for **reading** (and deleting). Using it as an assignment\ntarget is a **syntax error** — you can't conditionally write through it.\n\n```js\nconst obj = { a: {} }\n\u002F\u002F obj?.a?.b = 5                     \u002F\u002F SyntaxError — invalid assignment target\nif (obj?.a) obj.a.b = 5             \u002F\u002F guard, then assign normally\n```\n\n`delete obj?.a.b` is allowed (short-circuits to a no-op if `obj` is nullish), but\nassignment is not. Pitfall: people expect symmetric read\u002Fwrite support and are\nsurprised the parser rejects the assignment form outright.\n",{"id":245,"difficulty":68,"q":246,"a":247},"optional-chaining-overuse","What is the danger of over-using optional chaining?","Sprinkling `?.` everywhere **hides real bugs**. If a value should never be\n`null`, guarding it with `?.` silently swallows the case where it unexpectedly is,\nturning a loud crash into a quiet `undefined` that surfaces far away.\n\n```js\n\u002F\u002F a required user that's accidentally null:\nconst name = user?.profile?.name    \u002F\u002F becomes undefined, no signal something broke\nconst name2 = user.profile.name     \u002F\u002F throws loudly at the actual fault line\n```\n\nUse `?.` only where absence is **legitimate and expected**, not as a blanket\ncrash-suppressor. Pitfall: defensive `?.` everywhere defers the error to a\nconfusing downstream location, making bugs far harder to trace.\n",{"id":249,"difficulty":68,"q":250,"a":251},"nullish-default-vs-destructuring-default","How does ?? differ from a destructuring default?","Both default on `undefined`, but a **destructuring default** does **not** trigger\non `null`, whereas `??` does. So they diverge whenever a value is explicitly\n`null`.\n\n```js\nconst { x = 1 } = { x: null }\nconsole.log(x)                       \u002F\u002F null  \u003C- destructuring default ignores null\nconst y = ({ x: null }).x ?? 1\nconsole.log(y)                       \u002F\u002F 1     \u003C- ?? catches null too\n```\n\nUse a destructuring default for \"missing key,\" and `??` (or both) when `null` is\nalso a \"use the fallback\" signal. Pitfall: assuming `{ x = d }` and `x ?? d`\nbehave identically — they differ precisely on `null`.\n",{"id":253,"difficulty":35,"q":254,"a":255},"optional-call-on-array-method","How do you safely call a method on a possibly-missing array?","Chain `?.` before the method call: `arr?.map(...)`. If `arr` is `null`\u002F\n`undefined`, the whole call short-circuits to `undefined`.\n\n```js\nconst data = {}\nconst upper = data.tags?.map(t => t.toUpperCase())\nconsole.log(upper)                   \u002F\u002F undefined  no throw on missing tags\nconst safe = data.tags?.map(t => t) ?? []  \u002F\u002F fall back to empty array\n```\n\nPair with `?? []` if downstream code expects an array. Pitfall: `data.tags?.map`\nreturning `undefined` will break a chained `.filter()` after it — guard the whole\nchain or supply the `?? []` fallback.\n",{"id":257,"difficulty":35,"q":258,"a":259},"double-question-mark-with-function-call","Does the right-hand side of ?? always evaluate?","No — `??` **short-circuits**. The right operand evaluates **only** when the left\nis `null`\u002F`undefined`. So an expensive default or a function call on the right is\nskipped when the left has a value.\n\n```js\nlet computed = 0\nconst v = 'cached' ?? expensive()    \u002F\u002F expensive() never runs\nfunction expensive() { computed++; return 'x' }\nconsole.log(computed)                \u002F\u002F 0  skipped\n```\n\nThis makes `value ?? buildDefault()` cheap in the common case. Pitfall: relying\non the right side's side effects is a bug, since it may never execute.\n",{"id":261,"difficulty":68,"q":262,"a":263},"chaining-after-optional-call","How does a ?. short-circuit affect the rest of a long chain?","Once any `?.` short-circuits, the **entire remaining chain** — further property\naccesses, calls, and index lookups — is skipped, and the whole expression is\n`undefined`. The short-circuit \"infects\" everything to its right.\n\n```js\nconst obj = { a: null }\nconsole.log(obj.a?.b.c.d())          \u002F\u002F undefined  b.c.d() all skipped\n```\n\nYou don't need `?.` on every link **after** the one that may be nullish — the\nfirst short-circuit covers the rest. Pitfall: but each *independent* uncertain\nlink still needs its own `?.`; the short-circuit only protects links **downstream**\nof where it fired.\n",{"id":265,"difficulty":43,"q":266,"a":267},"nullish-with-numbers","Why is ?? recommended for numeric defaults?","Because `0` is a valid number but **falsy**, `||` would wrongly replace it.\n`??` only replaces `null`\u002F`undefined`, so a real `0` survives.\n\n```js\nconst volume = settings.volume ?? 100   \u002F\u002F volume of 0 stays 0\n\u002F\u002F const bad = settings.volume || 100   \u002F\u002F 0 becomes 100 — muted unmute!\n```\n\nSame reasoning applies to any field where `0`, `''`, or `false` are meaningful.\nPitfall: a \"default to 100\" written with `||` silently breaks the legitimate-zero\ncase — a classic slider\u002Fquantity bug.\n",{"id":269,"difficulty":68,"q":270,"a":271},"optional-chaining-with-this","Does optional chaining preserve the this binding in a method call?","Yes. `obj?.method()` keeps `this` bound to `obj`, exactly like a normal method\ncall — the `?.` only adds the nullish guard, it doesn't change the call's\nreceiver.\n\n```js\nconst counter = {\n  n: 5,\n  get() { return this.n }\n}\nconsole.log(counter?.get())          \u002F\u002F 5  this is still counter\n```\n\nThe short-circuit happens before the call, so `this` is irrelevant when it fires.\nPitfall: extracting the method first (`const g = counter?.get; g()`) **loses**\n`this`, just like without `?.` — optional chaining doesn't bind methods.\n",{"id":273,"difficulty":35,"q":274,"a":275},"nullish-chaining-multiple","How do you chain multiple ?? operators?","`a ?? b ?? c` returns the **first non-nullish** value left to right. It's a clean\nway to express a fallback priority list.\n\n```js\nconst value = userSetting ?? cookieSetting ?? defaultSetting ?? 'fallback'\nconsole.log(null ?? undefined ?? 0)  \u002F\u002F 0  first non-nullish wins (0 counts)\n```\n\nEach `??` short-circuits, so evaluation stops at the first real value. Pitfall:\nthis is valid (chaining `??` with `??` is fine), but mixing in a `||` mid-chain\nwithout parentheses is a syntax error.\n",{"id":277,"difficulty":68,"q":278,"a":279},"optional-delete","Can you use optional chaining with the delete operator?","Yes — `delete obj?.prop` deletes the property if `obj` exists, and is a **no-op**\n(returning `true`) if `obj` is `null`\u002F`undefined`. It's one of the few non-read\ncontexts `?.` allows.\n\n```js\nconst obj = { a: 1 }\ndelete obj?.a                        \u002F\u002F deletes a\nconst nothing = null\ndelete nothing?.x                    \u002F\u002F no-op, no throw\n```\n\nAssignment is still forbidden, but deletion is permitted because it short-circuits\ncleanly. Pitfall: it's an uncommon form, so reviewers may not realize the delete\nis conditionally skipped when the object is nullish.\n",{"id":281,"difficulty":35,"q":282,"a":283},"nullish-assignment-lazy","How is ??= useful for lazy initialization or caching?","Because `??=` only assigns (and only evaluates the right side) when the property is\nnullish, it's perfect for \"compute once and cache\" — subsequent calls skip the\nwork.\n\n```js\nconst cache = {}\nfunction get(key) {\n  return cache[key] ??= expensiveLookup(key)  \u002F\u002F computed once per key\n}\n```\n\nOn the first call `cache[key]` is `undefined`, so the lookup runs and stores;\nlater calls return the cached value without recomputing. Pitfall: if\n`expensiveLookup` can legitimately return `null`\u002F`undefined`, it's treated as\n\"not cached\" and re-runs every time.\n",{"id":285,"difficulty":35,"q":286,"a":287},"optional-chaining-falsy-confusion","Does ?. return undefined or the actual value when the chain succeeds?","When the chain **succeeds**, `?.` returns the real value — it does **not** convert\nanything. It only injects `undefined` when it actually short-circuits on a\nnullish link.\n\n```js\nconst obj = { a: 0, b: false }\nconsole.log(obj?.a)                  \u002F\u002F 0      real value\nconsole.log(obj?.b)                  \u002F\u002F false  real value, not undefined\nconsole.log(obj?.missing)            \u002F\u002F undefined  (short-circuit)\n```\n\nSo a result of `undefined` could mean \"short-circuited\" **or** \"property is\ngenuinely `undefined`.\" Pitfall: you can't distinguish those two cases from the\nresult alone — use `in` or `hasOwnProperty` if the difference matters.\n",{"id":289,"difficulty":35,"q":290,"a":291},"nullish-in-jsx-or-template","Why is ?? safer than || when rendering values in UI?","In templating\u002FJSX, falsy-but-valid values like `0` or `''` should render. `||`\nwould replace them with the fallback, hiding real data; `??` keeps them.\n\n```js\n\u002F\u002F showing a like count:\nconst display = post.likes ?? 'N\u002FA'  \u002F\u002F 0 renders as 0\n\u002F\u002F const bad = post.likes || 'N\u002FA'   \u002F\u002F 0 likes shows 'N\u002FA'\n```\n\nSame for displaying an empty search query or a `false` toggle label. Pitfall:\n`||` in a render path is a frequent source of \"why does zero show as N\u002FA?\" bug\nreports.\n",{"id":293,"difficulty":35,"q":294,"a":295},"optional-chaining-performance","Does optional chaining have meaningful performance cost?","Negligibly. `?.` compiles to a simple `null`\u002F`undefined` check before each\naccess — a tiny conditional. It's far cheaper than the bugs and verbose guards it\nreplaces.\n\n```js\n\u002F\u002F these are essentially equivalent in cost:\nconst a = obj && obj.prop\nconst b = obj?.prop                  \u002F\u002F same check, cleaner\n```\n\nThe \"cost\" worth watching is **semantic**, not CPU — readability and bug-hiding.\nPitfall: don't avoid `?.` for imagined perf reasons; the real concern is using it\nwhere a value should never be nullish (see over-use).\n",{"id":297,"difficulty":68,"q":298,"a":299},"combining-optional-call-and-nullish","How do you safely invoke an optional callback and provide a result default?","Combine `?.()` (skip the call if the function is absent) with `??` (default the\nresult if the call was skipped or returned nullish).\n\n```js\nfunction process(data, transform) {\n  return transform?.(data) ?? data   \u002F\u002F use transform if given, else raw data\n}\nprocess(5)                           \u002F\u002F 5  — no transform, falls back\nprocess(5, x => x * 2)               \u002F\u002F 10 — transform applied\n```\n\n`transform?.(data)` is `undefined` when no callback is passed, and `?? data`\nsupplies the fallback. Pitfall: if `transform` legitimately returns `0`\u002F`''`,\n`??` keeps it (good), but `|| data` would wrongly discard those.\n",{"id":301,"difficulty":35,"q":302,"a":303},"nullish-coalescing-with-assignment-target","Why prefer config.x ??= default over config.x = config.x ?? default?","They're equivalent in result, but `??=` is shorter and, crucially,\n**short-circuits the assignment**: when `config.x` already has a value, no write\nhappens — avoiding spurious setter calls or proxy traps.\n\n```js\n\u002F\u002F verbose:\nconfig.x = config.x ?? compute()\n\u002F\u002F concise + skips the write when x exists:\nconfig.x ??= compute()               \u002F\u002F\n```\n\nFor plain objects the difference is cosmetic; for objects with setters or\nreactive frameworks it avoids unnecessary side effects. Pitfall: on a getter-only\n(read-only) property, `??=` still attempts the write when nullish and throws in\nstrict mode.\n",{"id":305,"difficulty":68,"q":306,"a":307},"optional-chaining-with-bracket-call","How do you combine dynamic key access and optional method calls in one chain?","You can mix `?.[key]` and `?.()` freely in a single chain; each `?.` guards its\nown link, and the first nullish one short-circuits the rest.\n\n```js\nconst handlers = { onSave: () => 'saved' }\nconst name = 'onSave'\nconsole.log(handlers?.[name]?.())    \u002F\u002F 'saved'\nconsole.log(handlers?.['onDelete']?.())  \u002F\u002F undefined  missing handler, no throw\n```\n\nThis pattern safely dispatches to a dynamically-named handler that may not exist.\nPitfall: `handlers[name]?.()` (no `?.` before `[name]`) still throws if\n`handlers` itself is nullish — guard the root too.\n",{"description":32},"JavaScript optional chaining and nullish coalescing interview questions — the ?. operator on properties, methods, and calls, ?? vs ||, logical assignment operators ??= ||= &&=, short-circuiting, and precedence pitfalls.","javascript\u002Fmodern\u002Foptional-chaining-nullish","Optional Chaining & Nullish Coalescing","WX3Bq31uUWq16_jGkIHCQ3CHmeRYaov98DiBBBbqeLY",{"id":314,"title":315,"body":316,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":320,"navigation":38,"order":321,"path":322,"questions":323,"related":171,"seo":444,"seoDescription":445,"stem":446,"subtopic":447,"topic":19,"topicSlug":21,"updated":176,"__hash__":448},"qa\u002Fjavascript\u002Fmodern\u002Ftemplate-literals.md","Template Literals",{"type":29,"value":317,"toc":318},[],{"title":32,"searchDepth":33,"depth":33,"links":319},[],{},3,"\u002Fjavascript\u002Fmodern\u002Ftemplate-literals",[324,328,332,336,340,344,348,352,356,360,364,368,372,376,380,384,388,392,396,400,404,408,412,416,420,424,428,432,436,440],{"id":325,"difficulty":43,"q":326,"a":327},"template-literal-basics","What is a template literal?","A **template literal** is a string written with **backticks** (`` ` ``)\nthat supports interpolation and spans multiple lines.\n\n```js\nconst name = 'Ada'\nconst msg = `Hello, ${name}!`   \u002F\u002F \"Hello, Ada!\"\n```\n\nBackticks replace the need for string concatenation and make embedded\nvalues far more readable than `'Hello, ' + name + '!'`.\n",{"id":329,"difficulty":43,"q":330,"a":331},"interpolation","How does ${} interpolation work?","Any expression inside **`${ }`** is evaluated and its result coerced to a\nstring, then spliced into place.\n\n```js\nconst a = 5, b = 3\n`Sum is ${a + b}`         \u002F\u002F \"Sum is 8\"\n`Upper: ${name.toUpperCase()}`  \u002F\u002F method calls allowed\n```\n\nIt's a full expression slot — arithmetic, calls, ternaries, even nested\ntemplate literals all work. It is **not** a statement slot, so `if`\u002F`for`\ncan't go there.\n",{"id":333,"difficulty":43,"q":334,"a":335},"multiline-strings","How do template literals handle multiline strings?","Newlines inside backticks are **preserved literally** — no `\\n` or\nconcatenation needed.\n\n```js\nconst text = `Line 1\nLine 2`         \u002F\u002F contains a real newline\n```\n\nPitfall: indentation inside the literal becomes part of the string. Leading\nspaces on the second line are included, which can surprise you when the\ncode is nested deep in a function.\n",{"id":337,"difficulty":35,"q":338,"a":339},"expression-embedding","What kinds of expressions can you embed?","Any valid JavaScript **expression**: arithmetic, function calls, ternaries,\nproperty access, even another template literal.\n\n```js\n`Status: ${active ? 'on' : 'off'}`\n`Total: ${items.reduce((s, i) => s + i.price, 0)}`   \u002F\u002F\n```\n\nKeep them simple, though — heavy logic inside `${}` hurts readability.\nCompute into a variable first when the expression gets complex.\n",{"id":341,"difficulty":35,"q":342,"a":343},"nested-templates","Can you nest template literals?","Yes — a `${}` slot can contain another backtick string, which is useful for\nconditional fragments.\n\n```js\nconst html = `\u003Cul>${items.map(i => `\u003Cli>${i}\u003C\u002Fli>`).join('')}\u003C\u002Ful>`\n```\n\nIt works, but deeply nested templates become hard to read. For non-trivial\nmarkup, prefer a templating library or break the pieces into named\nvariables.\n",{"id":345,"difficulty":35,"q":346,"a":347},"tagged-template-basics","What is a tagged template?","A **tagged template** calls a function with the literal's parts. The tag\nfunction receives the array of **string segments** first, then each\ninterpolated **value** as subsequent arguments.\n\n```js\nfunction tag(strings, ...values) {\n  return { strings, values }\n}\ntag`Hi ${name}, you are ${age}`\n\u002F\u002F strings: ['Hi ', ', you are ', ''], values: [name, age]\n```\n\nThe tag fully controls the output — it doesn't have to return a string at\nall. This powers libraries like styled-components and graphql-tag.\n",{"id":349,"difficulty":35,"q":350,"a":351},"tagged-template-strings-values","Why does the strings array always have one more element than values?","Because the string segments **surround** the interpolations — there's\nalways a piece before the first `${}` and after the last one (possibly\nempty).\n\n```js\ntag`a${1}b${2}c`\n\u002F\u002F strings: ['a', 'b', 'c']  (3)\n\u002F\u002F values:  [1, 2]           (2)  strings.length === values.length + 1\n```\n\nTag functions rely on this invariant to interleave: zip\n`strings[i]` with `values[i]` and append the final `strings[n]`.\n",{"id":353,"difficulty":35,"q":354,"a":355},"string-raw","What does String.raw do?","**`String.raw`** is a built-in tag that returns the string with escape\nsequences **uninterpreted** — backslashes stay literal.\n\n```js\nString.raw`C:\\new\\test`   \u002F\u002F 'C:\\\\new\\\\test' (backslashes kept)\n`C:\\new\\test`             \u002F\u002F 'C:' + newline + 'ew' + tab + 'est'\n```\n\nGreat for Windows paths, regex source, and LaTeX. The `strings.raw`\nproperty is how any custom tag accesses the un-escaped segments.\n",{"id":357,"difficulty":68,"q":358,"a":359},"raw-property","What is the .raw property on the strings array?","Inside a tag, `strings` also carries a **`raw`** array holding the\nsegments **before** escape processing — so the tag can choose cooked or raw\ntext.\n\n```js\nfunction show(strings) {\n  return strings.raw[0]   \u002F\u002F raw, e.g. '\\\\n' stays as backslash-n\n}\nshow`\\n`   \u002F\u002F '\\\\n'  not an actual newline\n```\n\n`String.raw` is literally implemented by joining `strings.raw` with the\nvalues.\n",{"id":361,"difficulty":35,"q":362,"a":363},"i18n-use-case","How are tagged templates used for internationalization?","A tag can rebuild the message from its parts, looking up a translated\ntemplate by the static `strings` segments and re-inserting the dynamic\nvalues.\n\n```js\nfunction i18n(strings, ...values) {\n  const key = strings.join('{}')          \u002F\u002F stable lookup key\n  const translated = dictionary[key] ?? key\n  return interpolate(translated, values)   \u002F\u002F locale-aware\n}\ni18n`Hello ${name}`\n```\n\nBecause the static parts are constant per call site, they make a reliable\ntranslation key.\n",{"id":365,"difficulty":68,"q":366,"a":367},"html-escaping-tag","How can a tagged template prevent injection?","The tag escapes the **interpolated values** while leaving the static\n`strings` (trusted, author-written) untouched — exactly what raw template\nliterals fail to do.\n\n```js\nfunction safeHtml(strings, ...values) {\n  return strings.reduce((out, s, i) =>\n    out + s + (i \u003C values.length ? escapeHtml(values[i]) : ''), '')\n}\nsafeHtml`\u003Cp>${userInput}\u003C\u002Fp>`   \u002F\u002F userInput is escaped\n```\n\nThis is the secure pattern; plain `` `\u003Cp>${userInput}\u003C\u002Fp>` `` injects raw\nuser input.\n",{"id":369,"difficulty":68,"q":370,"a":371},"injection-risk","What's the security pitfall of plain template literals?","They perform **no escaping** — interpolated values go in verbatim. Building\nHTML or SQL by interpolation invites XSS\u002FSQL injection.\n\n```js\nel.innerHTML = `\u003Cdiv>${userInput}\u003C\u002Fdiv>`   \u002F\u002F XSS if userInput has \u003Cscript>\ndb.query(`SELECT * FROM u WHERE id = ${id}`) \u002F\u002F SQL injection\n```\n\nUse a sanitizing tag for HTML, and **parameterized queries** for SQL —\nnever interpolate untrusted data into markup or queries directly.\n",{"id":373,"difficulty":43,"q":374,"a":375},"concatenation-comparison","Why prefer template literals over string concatenation?","They're more readable, avoid `+` clutter and stray-space bugs, and handle\nmultiline naturally.\n\n```js\n'Hi ' + name + ', you have ' + n + ' items'   \u002F\u002F noisy\n`Hi ${name}, you have ${n} items`             \u002F\u002F clear\n```\n\nConcatenation also silently coerces with `+`, where a misplaced operand can\nproduce `NaN` or \"[object Object]\"; template slots make intent explicit.\n",{"id":377,"difficulty":35,"q":378,"a":379},"coercion-in-template","How are non-string values coerced inside a template?","Each `${}` result is converted with the abstract ToString — objects call\n`toString()`, arrays join with commas, `null`\u002F`undefined` stringify\nliterally.\n\n```js\n`${[1, 2, 3]}`    \u002F\u002F '1,2,3'\n`${ {a: 1} }`     \u002F\u002F '[object Object]'  rarely what you want\n`${null}`         \u002F\u002F 'null'\n```\n\nFor objects you usually want `JSON.stringify(obj)` inside the slot rather\nthan relying on the default `[object Object]`.\n",{"id":381,"difficulty":43,"q":382,"a":383},"escaping-backtick","How do you include a backtick or ${ literally?","Escape them with a backslash.\n\n```js\n`A backtick: \\``        \u002F\u002F \"A backtick: `\"\n`Not a slot: \\${x}`     \u002F\u002F \"Not a slot: ${x}\"  literal\n```\n\nWithout the backslash, `${x}` would be interpreted as interpolation. This\nmatters when generating template-literal source code or documentation.\n",{"id":385,"difficulty":35,"q":386,"a":387},"css-in-js","How do tagged templates enable CSS-in-JS?","Libraries like styled-components expose a tag that turns the literal into a\nstyled component, injecting interpolated values as dynamic style props.\n\n```js\nconst Button = styled.button`\n  color: ${props => props.primary ? 'white' : 'black'};\n`   \u002F\u002F tag processes the CSS + functions\n```\n\nThe tag receives the CSS chunks and the prop-accessor functions, then\ngenerates real stylesheets at render time.\n",{"id":389,"difficulty":35,"q":390,"a":391},"graphql-gql","Why does GraphQL use a gql tagged template?","The `gql` tag parses the query string into an **AST** at definition time,\ncatching syntax errors early and enabling tooling (highlighting, type\ngeneration) on the embedded query.\n\n```js\nconst QUERY = gql`\n  query { user { id name } }\n`   \u002F\u002F parsed, not just a string\n```\n\nIt also lets fragments be interpolated and composed via `${}` while keeping\na single parse step.\n",{"id":393,"difficulty":68,"q":394,"a":395},"performance-static-strings","Are the strings arrays in tagged templates cached?","Yes — for a given tagged-template **call site**, the engine reuses the\n**same frozen `strings` array** across invocations. This lets tags memoize\nby identity.\n\n```js\nfunction tag(strings) { \u002F* strings is the same object each call *\u002F }\nfunction run() { return tag`hello` }\nrun() ; run()   \u002F\u002F both calls receive the identical strings reference\n```\n\nLibraries exploit this to cache parsed templates (e.g. compiled CSS or\nGraphQL) keyed on the `strings` object.\n",{"id":397,"difficulty":35,"q":398,"a":399},"dedent-indentation","How do you handle unwanted indentation in multiline templates?","The literal preserves source indentation, so nested code leaks leading\nspaces. Use a **dedent** tag or post-process with a regex.\n\n```js\nconst sql = `\n  SELECT *\n  FROM users\n`   \u002F\u002F each line has leading spaces\n\nconst clean = sql.replace(\u002F^\\s+\u002Fgm, '')   \u002F\u002F strip leading whitespace\n```\n\nDedent helper tags are common in test fixtures and embedded SQL\u002FGraphQL to\nkeep output clean while keeping source readable.\n",{"id":401,"difficulty":43,"q":402,"a":403},"ternary-in-slot","How do you add conditional content inside a template?","Use a **ternary** or short-circuit expression in the slot — statements\naren't allowed.\n\n```js\n`Cart${count !== 1 ? 's' : ''}`           \u002F\u002F pluralize\n`${error ? `Error: ${error}` : 'OK'}`     \u002F\u002F nested + conditional\n```\n\nFor anything beyond a simple ternary, compute the fragment in a variable\nfirst to keep the literal readable.\n",{"id":405,"difficulty":35,"q":406,"a":407},"arrays-join-in-template","How do you render a list inside a template literal?","Map to strings and **`join`** — relying on default array coercion adds\ncommas you usually don't want.\n\n```js\n`\u003Cul>${items.map(i => `\u003Cli>${i}\u003C\u002Fli>`).join('')}\u003C\u002Ful>`   \u002F\u002F\n`\u003Cul>${items}\u003C\u002Ful>`   \u002F\u002F inserts commas between items\n```\n\nThe explicit `.join('')` (or `.join(', ')`) controls the separator instead\nof getting the implicit comma.\n",{"id":409,"difficulty":35,"q":410,"a":411},"template-vs-format","Do template literals support format specifiers like printf?","No — there's no `%d`\u002F`%s` formatting. You format inside the slot with\nmethods or `Intl`.\n\n```js\n`Price: ${value.toFixed(2)}`                       \u002F\u002F 2 decimals\n`Date: ${new Intl.DateTimeFormat('en').format(d)}` \u002F\u002F locale format\n```\n\nFor padding\u002Fnumber formatting use `padStart`, `toLocaleString`, or `Intl.*`\nrather than expecting printf-style directives.\n",{"id":413,"difficulty":43,"q":414,"a":415},"empty-interpolation","What happens with an empty or whitespace ${} slot?","`${}` with nothing is a **SyntaxError** — a slot must contain an\nexpression. Whitespace alone is also invalid.\n\n```js\n`${}`        \u002F\u002F SyntaxError\n`${ '' }`    \u002F\u002F an empty-string expression is fine\n`${ \u002F* x *\u002F undefined }`  \u002F\u002F valid, inserts 'undefined'\n```\n\nYou need a real expression; if you want nothing, interpolate an empty\nstring `''`.\n",{"id":417,"difficulty":68,"q":418,"a":419},"tagged-non-string-return","Can a tagged template return something other than a string?","Yes — the tag's return value is whatever the function returns. It can be an\nobject, a component, a parsed AST, a function, anything.\n\n```js\nfunction obj(strings, ...values) {\n  return { template: strings, data: values }   \u002F\u002F returns an object\n}\nconst x = obj`a ${1}`   \u002F\u002F x is an object, not a string\n```\n\nThis flexibility is the whole point — tags are a DSL hook, not just string\nformatters.\n",{"id":421,"difficulty":35,"q":422,"a":423},"line-continuation","How do you write a long single-line string without a literal newline?","End a line with a **backslash** to continue without inserting a newline.\n\n```js\nconst s = `This is one long \\\nlogical line`   \u002F\u002F no newline between 'long ' and 'logical'\n```\n\nBe careful: any spaces after the backslash break it. This is occasionally\nhandy, but joining string parts or using normal wrapping is usually\nclearer.\n",{"id":425,"difficulty":35,"q":426,"a":427},"template-with-functions","Can you call a function and use its result in a slot?","Yes — slots are expressions, so calls (including IIFEs) are allowed and\ntheir return value is interpolated.\n\n```js\n`Now: ${getTime()}`\n`Result: ${(() => compute())()}`   \u002F\u002F IIFE result\n```\n\nSide effects run at interpolation time, in left-to-right order with the\nother slots — keep that in mind if a slot mutates state.\n",{"id":429,"difficulty":35,"q":430,"a":431},"comparison-with-sprintf","When might you still want a formatting library instead of templates?","For locale-aware numbers\u002Fdates, complex pluralization (ICU MessageFormat),\nor printf-style alignment, dedicated libraries (`Intl`, `messageformat`) do\nwhat raw templates can't.\n\n```js\n\u002F\u002F pluralization rules vary by language — a template can't express them all\nmessageFormat.format({ count })   \u002F\u002F handles one\u002Ffew\u002Fmany per locale\n```\n\nTemplates excel at simple interpolation and embedded DSLs; reach for\nformatting libs when human-language or numeric formatting gets involved.\n",{"id":433,"difficulty":35,"q":434,"a":435},"jsx-relation","Are template literals related to JSX?","No — JSX is a separate compile-time syntax transformed by Babel\u002FTypeScript,\nnot a runtime template literal. But tagged templates (`html\\`...\\``) are\nthe **JSX-free** alternative used by libraries like lit-html.\n\n```js\n\u002F\u002F lit-html: tagged template, no build step needed\nhtml`\u003Cdiv>${content}\u003C\u002Fdiv>`   \u002F\u002F runtime, parsed by the html tag\n```\n\nSo tagged templates give a \"JSX-like\" templating experience without a\ncompiler.\n",{"id":437,"difficulty":68,"q":438,"a":439},"whitespace-control","How do you control whitespace between interpolations?","Whatever sits between `${}` slots in the source is kept exactly, so you\nmanage spacing by what you type — or strip it in a tag.\n\n```js\n`${a} ${b}`     \u002F\u002F single space between\n`${a}${b}`      \u002F\u002F no space\n`${a}\n${b}`           \u002F\u002F newline preserved between them\n```\n\nFor HTML where whitespace collapses anyway it rarely matters; for\nprecise output (CSV, fixed-width), a normalizing tag gives full control.\n",{"id":441,"difficulty":43,"q":442,"a":443},"best-practices","What are best practices for template literals?","Keep `${}` expressions simple, never interpolate untrusted data into\nHTML\u002FSQL without escaping\u002Fparameters, use `String.raw` for paths\u002Fregex, and\nmind leaked indentation in multiline strings.\n\n```js\nconst label = `${count} ${count === 1 ? 'item' : 'items'}`  \u002F\u002F simple\nel.textContent = `Hello ${name}`   \u002F\u002F textContent, not innerHTML\n```\n\nPrefer `textContent` over `innerHTML`, and a sanitizing tag when raw markup\nis unavoidable.\n",{"description":32},"JavaScript template literal interview questions — interpolation, multiline strings, expression embedding, tagged templates, String.raw and security pitfalls around escaping.","javascript\u002Fmodern\u002Ftemplate-literals","Template Literals & Tagged Templates","omSaAPQVGjJbVBYUSbK9SnLVjjpYF_CcFJGLpbxUZSU",{"id":450,"title":451,"body":452,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":456,"navigation":38,"order":457,"path":458,"questions":459,"related":171,"seo":576,"seoDescription":577,"stem":578,"subtopic":451,"topic":19,"topicSlug":21,"updated":176,"__hash__":579},"qa\u002Fjavascript\u002Fmodern\u002Fsymbols.md","Symbols",{"type":29,"value":453,"toc":454},[],{"title":32,"searchDepth":33,"depth":33,"links":455},[],{},4,"\u002Fjavascript\u002Fmodern\u002Fsymbols",[460,464,468,472,476,480,484,488,492,496,500,504,508,512,516,520,524,528,532,536,540,544,548,552,556,560,564,568,572],{"id":461,"difficulty":43,"q":462,"a":463},"what-is-symbol","What is a Symbol?","A **Symbol** is a primitive type (added in ES6) whose every value is\n**guaranteed unique**. You create one by calling `Symbol()`.\n\n```js\nconst a = Symbol()\nconst b = Symbol()\na === b   \u002F\u002F false  always unique, even with no description\ntypeof a  \u002F\u002F 'symbol'\n```\n\nTheir main use is as collision-proof object property keys and as\n\"well-known\" hooks that customize language behavior.\n",{"id":465,"difficulty":43,"q":466,"a":467},"symbol-description","What is the symbol description for?","The optional string passed to `Symbol('desc')` is a **debug label** only —\nit does **not** affect identity. Two symbols with the same description are\nstill different.\n\n```js\nconst s = Symbol('userId')\ns.description   \u002F\u002F 'userId'  read-only label\nSymbol('x') === Symbol('x')   \u002F\u002F false\n```\n\nUse it to make logs and `toString()` output readable; never rely on it for\nequality.\n",{"id":469,"difficulty":43,"q":470,"a":471},"symbol-no-new","Why can't you call Symbol with new?","`Symbol` is **not a constructor** — symbols are primitives, not objects, so\n`new Symbol()` throws a TypeError.\n\n```js\nconst s = Symbol('ok')      \u002F\u002F\nconst bad = new Symbol()    \u002F\u002F TypeError: Symbol is not a constructor\n```\n\nThis deliberately prevents accidental Symbol *wrapper objects*. If you ever\nneed the wrapper, `Object(symbol)` does it, but you rarely should.\n",{"id":473,"difficulty":35,"q":474,"a":475},"symbol-as-key","How do you use a Symbol as an object key?","Use **computed property** syntax (`[sym]`) in a literal, or bracket\nassignment. The key is then accessible only by holding that exact symbol.\n\n```js\nconst id = Symbol('id')\nconst user = { [id]: 123, name: 'Ada' }\nuser[id]    \u002F\u002F 123\nuser.id     \u002F\u002F undefined — 'id' string is a different key\n```\n\nBecause the symbol is unique, this key can never clash with any string key\nor another library's symbol.\n",{"id":477,"difficulty":35,"q":478,"a":479},"symbol-collision-free","Why are symbols useful for avoiding property collisions?","When you attach metadata to objects you don't own (or that mix data from\nmany sources), a symbol key is guaranteed not to overwrite or be overwritten\nby anyone else's key.\n\n```js\nconst CACHE = Symbol('cache')\nfunction memoize(obj) { obj[CACHE] = compute() }  \u002F\u002F won't clash\n```\n\nA string key like `obj.cache` risks stomping a real property; the symbol\ncan't, because no other code holds that symbol.\n",{"id":481,"difficulty":35,"q":482,"a":483},"symbol-not-enumerable","Are symbol-keyed properties enumerable?","Symbol keys are **skipped** by `for...in`, `Object.keys`, `Object.values`,\n`Object.entries`, and `JSON.stringify` — they're effectively hidden from\nordinary enumeration.\n\n```js\nconst s = Symbol('hidden')\nconst o = { [s]: 1, visible: 2 }\nObject.keys(o)       \u002F\u002F ['visible']  symbol omitted\nJSON.stringify(o)    \u002F\u002F '{\"visible\":2}'\n```\n\nThis makes symbols handy for \"internal\" properties that shouldn't leak into\nserialization or iteration.\n",{"id":485,"difficulty":35,"q":486,"a":487},"getownpropertysymbols","How do you retrieve symbol keys from an object?","Use **`Object.getOwnPropertySymbols`**, or `Reflect.ownKeys` to get string\nand symbol keys together.\n\n```js\nconst s = Symbol('id')\nconst o = { [s]: 1, name: 'x' }\nObject.getOwnPropertySymbols(o)  \u002F\u002F [Symbol(id)]\nReflect.ownKeys(o)               \u002F\u002F ['name', Symbol(id)]\n```\n\nSo symbols are **not truly private** — code with a reference to the object\ncan still discover and read them.\n",{"id":489,"difficulty":35,"q":490,"a":491},"symbol-for","What is Symbol.for and the global symbol registry?","**`Symbol.for(key)`** looks up (or creates) a symbol in a process-wide\n**global registry** by string key — so the *same* key always returns the\n*same* symbol, even across modules or realms.\n\n```js\nSymbol.for('app.id') === Symbol.for('app.id')   \u002F\u002F true  shared\nSymbol('app.id')     === Symbol('app.id')        \u002F\u002F false — local\n```\n\nUse `Symbol.for` when different parts of a system must agree on one symbol;\nuse plain `Symbol()` for guaranteed-unique local keys.\n",{"id":493,"difficulty":35,"q":494,"a":495},"symbol-keyfor","What does Symbol.keyFor do?","`Symbol.keyFor(sym)` returns the registry **key string** for a symbol\ncreated via `Symbol.for`, or `undefined` for a non-registered symbol.\n\n```js\nconst g = Symbol.for('shared')\nSymbol.keyFor(g)            \u002F\u002F 'shared'\nSymbol.keyFor(Symbol('x')) \u002F\u002F undefined — not in registry\n```\n\nIt's the inverse of `Symbol.for` and the only way to recover the key of a\nregistered symbol.\n",{"id":497,"difficulty":35,"q":498,"a":499},"well-known-symbols","What are well-known symbols?","They are built-in symbols exposed as static properties on `Symbol` (e.g.\n`Symbol.iterator`, `Symbol.toPrimitive`) that let you **hook into language\noperations** by defining specially-keyed methods on your objects.\n\n```js\nconst range = {\n  [Symbol.iterator]() { \u002F* ... *\u002F }   \u002F\u002F makes object iterable\n}\n```\n\nImplementing the right well-known symbol customizes how your object behaves\nin `for...of`, `instanceof`, type coercion, spreading, and more.\n",{"id":501,"difficulty":35,"q":502,"a":503},"symbol-iterator","How does Symbol.iterator make an object iterable?","Defining a `[Symbol.iterator]()` method that returns an iterator lets your\nobject work with `for...of`, spread, and destructuring.\n\n```js\nconst nums = {\n  *[Symbol.iterator]() { yield 1; yield 2; yield 3 }\n}\n[...nums]   \u002F\u002F [1, 2, 3]\n```\n\nAll built-in iterables (Array, Map, Set, String) implement this symbol — it\nis *the* protocol that makes iteration extensible.\n",{"id":505,"difficulty":68,"q":506,"a":507},"symbol-asynciterator","What is Symbol.asyncIterator?","It's the async counterpart of `Symbol.iterator` — defining\n`[Symbol.asyncIterator]()` makes an object usable with **`for await...of`**,\nwhere each `next()` returns a Promise.\n\n```js\nconst stream = {\n  async *[Symbol.asyncIterator]() { yield await fetchChunk() }\n}\nfor await (const chunk of stream) { \u002F* ... *\u002F }   \u002F\u002F\n```\n\nUsed for streams and paginated\u002Fasync data sources where values arrive over\ntime.\n",{"id":509,"difficulty":68,"q":510,"a":511},"symbol-toprimitive","How does Symbol.toPrimitive customize coercion?","Defining `[Symbol.toPrimitive](hint)` lets an object control its conversion\nto a primitive. The `hint` is `'number'`, `'string'`, or `'default'`\ndepending on context.\n\n```js\nconst money = {\n  amount: 5,\n  [Symbol.toPrimitive](hint) {\n    return hint === 'string' ? `$${this.amount}` : this.amount\n  }\n}\n`${money}`   \u002F\u002F '$5'   (string hint)\nmoney * 2    \u002F\u002F 10     (number hint)\n```\n\nIt overrides the default `valueOf`\u002F`toString` dance with a single, precise\nhook.\n",{"id":513,"difficulty":68,"q":514,"a":515},"symbol-tostringtag","What does Symbol.toStringTag control?","It customizes the tag shown by **`Object.prototype.toString`** — the\n`[object X]` label.\n\n```js\nclass Money {\n  get [Symbol.toStringTag]() { return 'Money' }\n}\nObject.prototype.toString.call(new Money())  \u002F\u002F '[object Money]'\n```\n\nBuilt-ins use it too (`[object Map]`, `[object Promise]`). It's mostly for\ndebugging and type-branding, not control flow.\n",{"id":517,"difficulty":68,"q":518,"a":519},"symbol-hasinstance","How can Symbol.hasInstance customize instanceof?","Defining a static `[Symbol.hasInstance](value)` method overrides what\n`value instanceof Class` returns — letting you do duck-typing checks.\n\n```js\nclass Even {\n  static [Symbol.hasInstance](n) { return n % 2 === 0 }\n}\n4 instanceof Even    \u002F\u002F true\n3 instanceof Even    \u002F\u002F false\n```\n\nPowerful but surprising — override `instanceof` sparingly, since readers\nassume it checks the prototype chain.\n",{"id":521,"difficulty":35,"q":522,"a":523},"private-fields-pattern","Are symbols a way to create private properties?","They provide **soft privacy**: symbol keys are hidden from enumeration and\n`JSON.stringify`, so they won't accidentally leak — but anyone with the\nsymbol (or via `Object.getOwnPropertySymbols`) can still access them.\n\n```js\nconst _secret = Symbol('secret')\nclass Vault { constructor() { this[_secret] = 42 } }\n```\n\nFor **hard** privacy use class `#private` fields, which are truly\ninaccessible from outside. Symbols are for collision-avoidance, not\nsecurity.\n",{"id":525,"difficulty":35,"q":526,"a":527},"symbols-and-json","What happens to symbols in JSON.stringify?","Symbol-**keyed** properties are ignored entirely, and a symbol **value**\nbecomes `undefined` (so it's dropped from objects or turned to `null` in\narrays).\n\n```js\nJSON.stringify({ [Symbol('k')]: 1, a: 2 })  \u002F\u002F '{\"a\":2}'  symbol key gone\nJSON.stringify({ a: Symbol('v') })          \u002F\u002F '{}'       symbol value gone\nJSON.stringify([Symbol('v')])               \u002F\u002F '[null]'\n```\n\nSo symbols never survive serialization — keep that in mind for state you\nneed to persist.\n",{"id":529,"difficulty":43,"q":530,"a":531},"symbol-typeof","What does typeof return for a symbol?","**`'symbol'`** — it's its own primitive type alongside string, number,\nboolean, bigint, undefined, and object.\n\n```js\ntypeof Symbol()        \u002F\u002F 'symbol'\ntypeof Symbol.iterator \u002F\u002F 'symbol'\n```\n\nThat distinct `typeof` makes symbols easy to detect, e.g. when guarding a\nfunction that should reject symbol keys.\n",{"id":533,"difficulty":35,"q":534,"a":535},"symbol-to-string-error","Why does implicit string conversion of a symbol throw?","Symbols **don't auto-coerce** to strings to avoid silent bugs — implicit\nconversion (concatenation, template slots) throws a TypeError. You must\nconvert explicitly.\n\n```js\nconst s = Symbol('id')\n'' + s            \u002F\u002F TypeError\n`${s}`            \u002F\u002F TypeError\nString(s)         \u002F\u002F 'Symbol(id)'  explicit\ns.toString()      \u002F\u002F 'Symbol(id)'\n```\n\n`String(sym)` and `.toString()` are the safe, intentional conversions.\n",{"id":537,"difficulty":68,"q":538,"a":539},"symbols-not-cloned","How do symbol keys behave with Object.assign and spread?","They **are** copied — `Object.assign` and object spread copy *enumerable\nown* properties including symbol keys (symbol keys are enumerable for\ncopying purposes even though they're skipped by `for...in`\u002F`Object.keys`).\n\n```js\nconst s = Symbol('id')\nconst src = { [s]: 1, a: 2 }\nconst copy = { ...src }\ncopy[s]   \u002F\u002F 1  symbol key copied\n```\n\nSo spreading does preserve symbol-keyed data — a useful subtlety when you\nassumed they'd be dropped.\n",{"id":541,"difficulty":35,"q":542,"a":543},"symbols-as-constants","Why use symbols for enum-like constants?","Symbols make **unforgeable, collision-free** constants — each is unique, so\nyou can't accidentally match by an equal string literal.\n\n```js\nconst Status = { ACTIVE: Symbol('active'), DONE: Symbol('done') }\nif (task.status === Status.ACTIVE) { \u002F* ... *\u002F }\n```\n\nUnlike string enums, a stray `'active'` elsewhere won't equal\n`Status.ACTIVE`. The tradeoff: symbols don't serialize, so use string enums\nwhen values cross network\u002Fstorage boundaries.\n",{"id":545,"difficulty":68,"q":546,"a":547},"symbol-registry-cross-realm","Why is Symbol.for useful across realms or iframes?","The global registry is shared by string key across **realms** (iframes,\nworker boundaries, modules), so `Symbol.for('x')` yields a matching symbol\neverywhere, whereas a plain `Symbol()` from one realm is unique to it.\n\n```js\n\u002F\u002F iframe A and iframe B both run:\nSymbol.for('app.token')   \u002F\u002F same symbol identity in both\n```\n\nIt's the mechanism for agreeing on a symbol without sharing the actual\nreference.\n",{"id":549,"difficulty":43,"q":550,"a":551},"detect-symbol-support","How do you check if a value is a symbol?","Use `typeof value === 'symbol'`. Avoid `instanceof Symbol`, which is false\nfor primitive symbols.\n\n```js\nconst isSymbol = v => typeof v === 'symbol'\nisSymbol(Symbol())          \u002F\u002F true\nSymbol() instanceof Symbol  \u002F\u002F false primitive, not an instance\n```\n\nThe `typeof` check is the only reliable test for symbol primitives.\n",{"id":553,"difficulty":68,"q":554,"a":555},"well-known-species","What does Symbol.species control?","`Symbol.species` lets a class specify which **constructor** built-in methods\nlike `map`\u002F`filter`\u002F`slice` use to create derived instances.\n\n```js\nclass MyArray extends Array {\n  static get [Symbol.species]() { return Array }  \u002F\u002F map returns Array\n}\nconst r = new MyArray(1, 2, 3).map(x => x)\nr instanceof MyArray   \u002F\u002F false — it's a plain Array\n```\n\nIt's an advanced hook for subclassing built-ins; most code never needs it,\nbut it explains some surprising subclass behaviors.\n",{"id":557,"difficulty":35,"q":558,"a":559},"symbol-iterator-builtin","How do you access the built-in iterator of an array via its symbol?","Call the array's `[Symbol.iterator]()` to get its iterator directly — the\nsame one `for...of` uses.\n\n```js\nconst arr = ['a', 'b']\nconst it = arr[Symbol.iterator]()\nit.next()   \u002F\u002F { value: 'a', done: false }\nit.next()   \u002F\u002F { value: 'b', done: false }\n```\n\nUseful when you want manual control over iteration or to forward an\niterator to other consuming code.\n",{"id":561,"difficulty":68,"q":562,"a":563},"symbols-memory","Do registered symbols cause memory concerns?","Symbols from **`Symbol.for`** live in the global registry for the lifetime\nof the realm and are **never garbage-collected**, since the registry holds\nthem. Plain `Symbol()` values are collectible like any primitive once\nunreferenced.\n\n```js\nSymbol.for('keeps.living')   \u002F\u002F retained globally\nlet s = Symbol('local'); s = null  \u002F\u002F eligible for GC\n```\n\nPrefer plain symbols unless you genuinely need cross-module sharing, to\navoid unbounded registry growth.\n",{"id":565,"difficulty":35,"q":566,"a":567},"symbols-vs-private-fields","When choose symbols over class private fields?","Use **`#private` fields** for true encapsulation within a single class. Use\n**symbols** when you need a non-enumerable key that's shareable across\nmodules, attachable to objects you don't own, or used in mixins.\n\n```js\nclass A { #real = 1 }          \u002F\u002F hard-private, class-scoped\nconst META = Symbol('meta')    \u002F\u002F shareable, attach anywhere\nsomeExternalObj[META] = 'tag'  \u002F\u002F works on foreign objects\n```\n\nPrivacy -> `#`; collision-free extensibility -> symbols.\n",{"id":569,"difficulty":68,"q":570,"a":571},"symbol-default-hint","What is the 'default' hint in Symbol.toPrimitive?","The `'default'` hint is passed for operations that don't clearly want a\nnumber or string — chiefly **`+`** and **`==`** comparisons. You decide\nwhat makes sense.\n\n```js\nconst temp = {\n  c: 20,\n  [Symbol.toPrimitive](hint) {\n    if (hint === 'number') return this.c\n    if (hint === 'string') return `${this.c}°C`\n    return `${this.c}°C`   \u002F\u002F 'default' -> treat like string here\n  }\n}\ntemp + ''   \u002F\u002F '20°C'  (default hint)\n```\n\nMost objects map `'default'` to the same behavior as `'number'` or\n`'string'` depending on intent.\n",{"id":573,"difficulty":35,"q":574,"a":575},"symbols-best-practices","What are best practices for using symbols?","Use plain `Symbol()` for collision-free metadata keys; reserve `Symbol.for`\nfor genuinely shared cross-module symbols; don't treat symbols as a\nsecurity mechanism; and convert to string explicitly with `String(sym)`.\n\n```js\nconst TAG = Symbol('tag')        \u002F\u002F local, unique\nthirdPartyObj[TAG] = 'mine'      \u002F\u002F safe to attach\n```\n\nImplement well-known symbols (`iterator`, `toPrimitive`) when you want\nobjects to integrate with language features — that's where symbols add the\nmost value.\n",{"description":32},"JavaScript Symbol interview questions — unique primitives, the global registry, symbol keys hidden from enumeration, well-known symbols like Symbol.iterator and Symbol.toPrimitive, and metaprogramming use cases.","javascript\u002Fmodern\u002Fsymbols","1lhIA2OYk4Bowpjxzvv-jTPSUsW0Rn_D38uNmRtHFAw",1781808674571]