[{"data":1,"prerenderedAt":178},["ShallowReactive",2],{"qa-\u002Fjavascript\u002Fmodern\u002Fdestructuring-spread-rest":3},{"page":4,"siblings":162,"blog":175},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"related":153,"seo":154,"seoDescription":155,"stem":156,"subtopic":157,"topic":158,"topicSlug":159,"updated":160,"__hash__":161},"qa\u002Fjavascript\u002Fmodern\u002Fdestructuring-spread-rest.md","Destructuring Spread Rest",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","JavaScript","javascript",{},true,1,"\u002Fjavascript\u002Fmodern\u002Fdestructuring-spread-rest",[23,28,32,36,40,44,48,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149],{"id":24,"difficulty":25,"q":26,"a":27},"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":29,"difficulty":25,"q":30,"a":31},"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":33,"difficulty":25,"q":34,"a":35},"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":37,"difficulty":25,"q":38,"a":39},"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":41,"difficulty":14,"q":42,"a":43},"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":45,"difficulty":14,"q":46,"a":47},"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":49,"difficulty":50,"q":51,"a":52},"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":54,"difficulty":14,"q":55,"a":56},"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":58,"difficulty":14,"q":59,"a":60},"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":62,"difficulty":14,"q":63,"a":64},"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":66,"difficulty":14,"q":67,"a":68},"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":70,"difficulty":25,"q":71,"a":72},"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":74,"difficulty":25,"q":75,"a":76},"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":78,"difficulty":14,"q":79,"a":80},"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":82,"difficulty":50,"q":83,"a":84},"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":86,"difficulty":14,"q":87,"a":88},"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":90,"difficulty":50,"q":91,"a":92},"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":94,"difficulty":25,"q":95,"a":96},"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":98,"difficulty":14,"q":99,"a":100},"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":102,"difficulty":14,"q":103,"a":104},"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":106,"difficulty":14,"q":107,"a":108},"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":110,"difficulty":25,"q":111,"a":112},"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":114,"difficulty":14,"q":115,"a":116},"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":118,"difficulty":50,"q":119,"a":120},"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":122,"difficulty":14,"q":123,"a":124},"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":126,"difficulty":14,"q":127,"a":128},"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":130,"difficulty":14,"q":131,"a":132},"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":134,"difficulty":50,"q":135,"a":136},"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":138,"difficulty":14,"q":139,"a":140},"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":142,"difficulty":50,"q":143,"a":144},"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":146,"difficulty":14,"q":147,"a":148},"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":150,"difficulty":14,"q":151,"a":152},"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":11},"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","Modern JavaScript (ES6+)","modern","2026-06-18","j-ktPh8WpFhFjct2cigvLtRd0mYHyPvKzyn8tht5YrQ",[163,164,167,171],{"subtopic":157,"path":21,"order":20},{"subtopic":165,"path":166,"order":12},"Optional Chaining & Nullish Coalescing","\u002Fjavascript\u002Fmodern\u002Foptional-chaining-nullish",{"subtopic":168,"path":169,"order":170},"Template Literals & Tagged Templates","\u002Fjavascript\u002Fmodern\u002Ftemplate-literals",3,{"subtopic":172,"path":173,"order":174},"Symbols","\u002Fjavascript\u002Fmodern\u002Fsymbols",4,{"path":176,"title":177},"\u002Fblog\u002Fjavascript-destructuring-spread-rest","JavaScript Destructuring, Spread & Rest — The Complete Guide to Objects and Arrays",1781808675937]