[{"data":1,"prerenderedAt":174},["ShallowReactive",2],{"qa-\u002Fjavascript\u002Farrays\u002Fmutating-vs-nonmutating":3},{"page":4,"siblings":157,"blog":171},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"related":148,"seo":149,"seoDescription":150,"stem":151,"subtopic":152,"topic":153,"topicSlug":154,"updated":155,"__hash__":156},"qa\u002Fjavascript\u002Farrays\u002Fmutating-vs-nonmutating.md","Mutating Vs Nonmutating",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","JavaScript","javascript",{},true,"\u002Fjavascript\u002Farrays\u002Fmutating-vs-nonmutating",[22,27,31,35,39,43,47,51,55,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144],{"id":23,"difficulty":24,"q":25,"a":26},"what-is-mutation","easy","What does it mean for an array method to mutate the array?","A **mutating** method changes the **original array in place** rather than\nreturning a fresh copy. The variable still points to the same array, but its\ncontents have changed.\n\n```js\nconst arr = [1, 2, 3]\narr.push(4) \u002F\u002F mutates: arr is now [1, 2, 3, 4]\n```\n\nThis matters because other code holding a reference to the same array sees the\nchange too — a frequent source of surprising bugs in shared state.\n",{"id":28,"difficulty":14,"q":29,"a":30},"list-mutating","Which array methods mutate the original array?","The classic mutators all change the array in place:\n\n```text\npush, pop, shift, unshift  -> add\u002Fremove ends\nsplice                     -> insert\u002Fremove\u002Freplace anywhere\nsort, reverse              -> reorder\nfill, copyWithin           -> overwrite ranges\n```\n\nA memory aid: if a method changes **length or order**, it almost certainly\nmutates. The notable surprise is that **sort** and **reverse** mutate — many\nassume they return a new array like `map`.\n",{"id":32,"difficulty":14,"q":33,"a":34},"list-nonmutating","Which common array methods return a new array instead of mutating?","These leave the original untouched and return a **new array**:\n\n```text\nmap, filter, slice, concat, flat, flatMap\ntoSorted, toReversed, toSpliced, with   (ES2023)\n```\n\nPlus the spread copy `[...arr]`. As a rule, the **iteration\u002Ftransform** methods\nare non-mutating, while the **in-place edit** methods mutate. Knowing which is\nwhich is essential for writing predictable code.\n",{"id":36,"difficulty":24,"q":37,"a":38},"push-vs-concat","What is the difference between push and concat?","**push** mutates the array and returns the **new length**; **concat** returns a\n**new array** and leaves the original alone.\n\n```js\nconst a = [1, 2]\na.push(3)        \u002F\u002F a is [1,2,3], returns 3\nconst b = a.concat(4) \u002F\u002F b is [1,2,3,4], a unchanged\n```\n\nPitfall: people sometimes write `arr = arr.push(x)`, which sets `arr` to a\nnumber (the length). Use `concat` or spread when you want a new array\nreference.\n",{"id":40,"difficulty":14,"q":41,"a":42},"slice-vs-splice","What is the difference between slice and splice?","**slice** is **non-mutating**: it returns a shallow copy of a range. **splice**\n**mutates**: it removes\u002Finserts elements in place and returns the removed ones.\n\n```js\nconst arr = [1, 2, 3, 4]\narr.slice(1, 3)     \u002F\u002F [2, 3], arr unchanged\narr.splice(1, 2)    \u002F\u002F removes [2, 3], arr is now [1, 4]\n```\n\nThe names look alike but behave oppositely on mutation. Confusing them is a\nclassic bug — remember **slice = copy, splice = surgery**.\n",{"id":44,"difficulty":14,"q":45,"a":46},"sort-mutates","Does sort mutate the array?","Yes. **sort** sorts the array **in place** and also returns a reference to that\n**same** array — so both the original and the return value are sorted.\n\n```js\nconst arr = [3, 1, 2]\nconst sorted = arr.sort()\nsorted === arr \u002F\u002F true — same array\narr            \u002F\u002F [1, 2, 3] — original is changed\n```\n\nTo sort without mutating, copy first (`[...arr].sort()`) or use **toSorted()**\nin modern environments. The same applies to `reverse`.\n",{"id":48,"difficulty":14,"q":49,"a":50},"tosorted","What is toSorted and how does it differ from sort?","**toSorted** (ES2023) returns a **new sorted array** and leaves the original\nunchanged — the immutable counterpart of `sort`.\n\n```js\nconst arr = [3, 1, 2]\nconst sorted = arr.toSorted() \u002F\u002F [1, 2, 3]\narr                           \u002F\u002F [3, 1, 2] — untouched\n```\n\nIt takes the same optional comparator as `sort`. Use it to avoid the classic\n`[...arr].sort()` copy dance, especially in React state where mutating is a\nbug.\n",{"id":52,"difficulty":14,"q":53,"a":54},"toreversed-tospliced-with","What do toReversed, toSpliced, and with do?","They are the **immutable** versions of `reverse`, `splice`, and index\nassignment, all returning a **new array** (ES2023).\n\n```js\nconst arr = [1, 2, 3]\narr.toReversed()       \u002F\u002F [3, 2, 1], arr unchanged\narr.toSpliced(1, 1, 9) \u002F\u002F [1, 9, 3], arr unchanged\narr.with(0, 100)       \u002F\u002F [100, 2, 3], arr unchanged\n```\n\n`with(i, value)` is the immutable replacement for `arr[i] = value`. Together\nthese let you write update logic without copying first.\n",{"id":56,"difficulty":57,"q":58,"a":59},"why-mutation-bugs","hard","Why does mutation cause bugs in shared state?","When two variables reference the **same array**, mutating through one changes\nwhat the other sees. Functions that mutate their arguments cause **action at a\ndistance**.\n\n```js\nconst original = [1, 2, 3]\nconst sorted = original\nsorted.sort((a, b) => b - a)\noriginal \u002F\u002F [3, 2, 1] — caller's array got reordered\n```\n\nFrameworks like React rely on **referential equality** to detect changes;\nmutating in place keeps the same reference, so the UI may not re-render.\nReturning new arrays avoids both classes of bug.\n",{"id":61,"difficulty":24,"q":62,"a":63},"copy-array-ways","What are the ways to copy an array?","For a **shallow** copy, any of these work:\n\n```js\nconst a = [1, 2, 3]\nconst b = [...a]        \u002F\u002F spread (most common)\nconst c = a.slice()     \u002F\u002F no args = full copy\nconst d = Array.from(a) \u002F\u002F\nconst e = a.concat()    \u002F\u002F\n```\n\nAll four copy the top level only. If the array holds objects, the copies share\nthose objects — see deep copying for nested data.\n",{"id":65,"difficulty":14,"q":66,"a":67},"shallow-vs-deep","What is the difference between a shallow and a deep copy of an array?","A **shallow** copy duplicates the array but shares the **same nested object\nreferences**. A **deep** copy duplicates everything recursively.\n\n```js\nconst arr = [{ n: 1 }]\nconst shallow = [...arr]\nshallow[0].n = 99\narr[0].n \u002F\u002F 99 — nested object is shared\n\nconst deep = structuredClone(arr)\ndeep[0].n = 1\narr[0].n \u002F\u002F 99 — independent\n```\n\nSpread\u002Fslice are shallow; reach for `structuredClone` when nested data must be\nindependent.\n",{"id":69,"difficulty":14,"q":70,"a":71},"structuredclone","When and how do you use structuredClone for arrays?","**structuredClone** does a true **deep copy** of arrays containing nested\nobjects, arrays, Maps, Sets, Dates, and more — without the limitations of\nJSON tricks.\n\n```js\nconst data = [{ id: 1, tags: ['a'] }]\nconst copy = structuredClone(data) \u002F\u002F fully independent\n```\n\nCaveat: it **cannot clone functions, DOM nodes, or class instances** with\nmethods (it throws or drops the prototype). It's built into modern Node and all\ncurrent browsers.\n",{"id":73,"difficulty":14,"q":74,"a":75},"json-deep-copy","What are the pitfalls of JSON.parse(JSON.stringify(arr)) for deep copy?","It deep-copies plain data but **silently corrupts** anything non-JSON:\n\n```js\nconst arr = [{ d: new Date(), fn: () => {}, u: undefined, n: NaN }]\nJSON.parse(JSON.stringify(arr))\n\u002F\u002F Date -> string, fn -> dropped, undefined -> dropped, NaN -> null\n```\n\nIt also throws on **circular references**. Prefer `structuredClone` for deep\ncopies; reserve the JSON trick for arrays of plain, JSON-safe values where the\nconversions don't matter.\n",{"id":77,"difficulty":14,"q":78,"a":79},"immutable-add","How do you add an item to an array immutably?","Build a **new array** with spread instead of `push`\u002F`unshift`:\n\n```js\nconst arr = [1, 2, 3]\nconst appended = [...arr, 4]   \u002F\u002F add to end\nconst prepended = [0, ...arr]  \u002F\u002F add to start\narr \u002F\u002F [1, 2, 3] — unchanged\n```\n\nThis pattern is standard in Redux reducers and React `setState`, where mutating\nthe existing array would break change detection and time-travel debugging.\n",{"id":81,"difficulty":14,"q":82,"a":83},"immutable-remove","How do you remove an item from an array immutably?","Use **filter** (by value\u002Fpredicate) or **slice + spread** (by index) to produce\na new array.\n\n```js\nconst arr = [1, 2, 3, 4]\narr.filter(n => n !== 3)             \u002F\u002F [1, 2, 4] by value\nconst i = 2\n[...arr.slice(0, i), ...arr.slice(i + 1)] \u002F\u002F [1, 2, 4] by index\narr.toSpliced(i, 1)                  \u002F\u002F [1, 2, 4] ES2023\n```\n\nAvoid `splice`, which mutates. `toSpliced` is the cleanest modern option for\nindex-based removal.\n",{"id":85,"difficulty":14,"q":86,"a":87},"immutable-update","How do you update an item at an index immutably?","Use **map** with the index, or **with()** in modern environments — never\n`arr[i] = x`, which mutates.\n\n```js\nconst arr = [1, 2, 3]\narr.map((v, idx) => (idx === 1 ? 99 : v)) \u002F\u002F [1, 99, 3]\narr.with(1, 99)                            \u002F\u002F [1, 99, 3] ES2023\narr \u002F\u002F [1, 2, 3] — unchanged\n```\n\nFor arrays of objects, also spread the object: `arr.map(o => o.id === id ? {\n...o, done: true } : o)` to avoid mutating the nested object.\n",{"id":89,"difficulty":57,"q":90,"a":91},"freeze-array","Does Object.freeze make an array fully immutable?","It makes it **shallowly** immutable: you can't add, remove, or reassign\ntop-level elements, but **nested objects stay mutable**.\n\n```js\nconst arr = Object.freeze([{ n: 1 }])\narr.push(2)   \u002F\u002F throws in strict mode (or silently fails)\narr[0].n = 99 \u002F\u002F still works — nested object not frozen\n```\n\nFor deep immutability you must freeze recursively (a \"deep freeze\"). Also note\nmutating methods like `push` throw on a frozen array in strict mode.\n",{"id":93,"difficulty":14,"q":94,"a":95},"fill","What does fill do and does it mutate?","**fill** overwrites a range of the array with a static value **in place** — it\nmutates and returns the same array.\n\n```js\nconst arr = [1, 2, 3, 4]\narr.fill(0, 1, 3) \u002F\u002F [1, 0, 0, 4], arr mutated\nnew Array(3).fill(0) \u002F\u002F [0, 0, 0] — common init pattern\n```\n\nGotcha: filling with a **shared object** puts the **same reference** in every\nslot: `new Array(3).fill([])` gives three references to one array. Use\n`Array.from({length:3}, () => [])` for distinct objects.\n",{"id":97,"difficulty":57,"q":98,"a":99},"copywithin","What does copyWithin do?","**copyWithin(target, start, end)** copies a slice of the array to another\nposition **within the same array**, in place, without changing its length.\n\n```js\nconst arr = [1, 2, 3, 4, 5]\narr.copyWithin(0, 3) \u002F\u002F [4, 5, 3, 4, 5] — copy from index 3 to 0\n```\n\nIt mutates and is rarely used in app code — it exists mainly for high\nperformance buffer manipulation. Worth recognizing in interviews but seldom\nreached for in practice.\n",{"id":101,"difficulty":24,"q":102,"a":103},"reverse-mutates","Does reverse mutate the array?","Yes — **reverse** reverses the array **in place** and returns a reference to the\nsame array.\n\n```js\nconst arr = [1, 2, 3]\narr.reverse()\narr \u002F\u002F [3, 2, 1] — original changed\n```\n\nTo reverse without mutating, use `[...arr].reverse()` or **toReversed()**. This\nmutation surprises people who expect array methods to be functional like `map`.\n",{"id":105,"difficulty":24,"q":106,"a":107},"assignment-not-copy","Why doesn't const b = a create a copy of the array?","Arrays are **reference types**. Assignment copies the **reference**, not the\ndata, so both names point at the **same array**.\n\n```js\nconst a = [1, 2, 3]\nconst b = a\nb.push(4)\na \u002F\u002F [1, 2, 3, 4] — same array\n```\n\nTo get an independent array you must explicitly copy: `const b = [...a]`. This\nreference behavior underlies most accidental-mutation bugs.\n",{"id":109,"difficulty":14,"q":110,"a":111},"mutation-in-functions","How can passing an array to a function mutate the caller's array?","The function receives the **same reference**, so any mutating method affects the\ncaller's array.\n\n```js\nfunction addItem(arr) { arr.push('x') } \u002F\u002F mutates caller\nconst list = [1]\naddItem(list)\nlist \u002F\u002F [1, 'x']\n```\n\nWrite **pure** functions that return new arrays instead: `function addItem(arr)\n{ return [...arr, 'x'] }`. This keeps call sites free of surprises and makes the\ncode easier to test.\n",{"id":113,"difficulty":14,"q":114,"a":115},"spread-shallow","Is the spread operator a deep or shallow copy for arrays?","**Shallow.** Spread copies the top-level elements; nested objects\u002Farrays are\nstill **shared by reference**.\n\n```js\nconst arr = [[1], [2]]\nconst copy = [...arr]\ncopy[0].push(99)\narr[0] \u002F\u002F [1, 99] — inner array shared\n```\n\nFor nested data you need a deep copy (`structuredClone`) or to spread each\nlevel. Treat spread as a fast top-level clone, nothing more.\n",{"id":117,"difficulty":57,"q":118,"a":119},"react-mutation","Why must you avoid mutating arrays in React state?","React decides whether to re-render by comparing the **previous and next\nreference**. Mutating in place keeps the **same reference**, so React thinks\nnothing changed.\n\n```js\n\u002F\u002F mutation — same reference, no re-render\nitems.push(newItem); setItems(items)\n\u002F\u002F new reference — triggers re-render\nsetItems([...items, newItem])\n```\n\nAlways create a new array (`map`, `filter`, spread, `toSorted`, `with`) when\nupdating state so React sees the change.\n",{"id":121,"difficulty":14,"q":122,"a":123},"empty-array-length","How does setting arr.length mutate the array?","Assigning to **length** mutates in place: shrinking truncates elements,\ngrowing pads with empty slots, and `length = 0` empties the array.\n\n```js\nconst arr = [1, 2, 3, 4]\narr.length = 2 \u002F\u002F [1, 2] — truncated\narr.length = 0 \u002F\u002F [] — cleared\n```\n\n`arr.length = 0` is a fast in-place clear that affects every reference to the\narray. To clear without mutating shared references, reassign: `arr = []`.\n",{"id":125,"difficulty":24,"q":126,"a":127},"pop-shift-return","What do pop and shift return, and do they mutate?","Both **mutate** and return the **removed element**. `pop` removes from the end,\n`shift` from the start.\n\n```js\nconst arr = [1, 2, 3]\narr.pop()   \u002F\u002F returns 3, arr is [1, 2]\narr.shift() \u002F\u002F returns 1, arr is [2]\n```\n\nNote `shift`\u002F`unshift` are **O(n)** because every remaining element must be\nreindexed, whereas `push`\u002F`pop` are O(1). Prefer end operations in hot loops.\n",{"id":129,"difficulty":24,"q":130,"a":131},"concat-vs-spread","Are concat and spread interchangeable for combining arrays?","Mostly yes — both return a **new array** without mutating. Spread reads more\nnaturally; `concat` has one nicety: it accepts **non-array values** directly.\n\n```js\n[...a, ...b]      \u002F\u002F merge two arrays\na.concat(b)       \u002F\u002F same result\na.concat(b, 5, 6) \u002F\u002F concat flattens array args and appends values\n[...a, b]         \u002F\u002F pushes the array b as a single element\n```\n\nFor huge arrays `concat` can be marginally faster, but readability usually\nfavors spread.\n",{"id":133,"difficulty":57,"q":134,"a":135},"in-place-dedupe","How do you dedupe an array, mutating versus non-mutating?","The clean, **non-mutating** way uses a `Set`:\n\n```js\nconst unique = [...new Set(arr)] \u002F\u002F new array, order preserved\n```\n\nAn in-place dedupe is fiddly and error-prone (you'd `splice` while iterating,\nwhich shifts indices). Prefer the Set approach. Note it uses **SameValueZero**,\nso `NaN` is deduped correctly but distinct objects with equal contents are not.\n",{"id":137,"difficulty":14,"q":138,"a":139},"immutability-libraries","What problem do libraries like Immer solve for arrays?","They let you write **mutating-looking** code that actually produces a **new,\nimmutable** array under the hood, avoiding verbose spread chains for deeply\nnested updates.\n\n```js\nconst next = produce(state, draft => {\n  draft.items.push(newItem) \u002F\u002F looks like mutation\n}) \u002F\u002F state is untouched, next is a new tree\n```\n\nImmer tracks changes to a proxy \"draft\" and builds the new structure for you.\nIt's popular in Redux Toolkit precisely because deep immutable updates with\nspread get unwieldy.\n",{"id":141,"difficulty":57,"q":142,"a":143},"detect-mutation","How can you guard against accidental mutation during development?","Use **Object.freeze** in development to make accidental mutation throw, lean on\n**const** (which prevents reassignment, not mutation), and adopt lint rules or\nlibraries that enforce immutability.\n\n```js\nconst config = Object.freeze([1, 2, 3])\nconfig.push(4) \u002F\u002F throws in strict mode — caught early\n```\n\nIn TypeScript, `readonly` arrays and `as const` give **compile-time**\nprotection with zero runtime cost, catching mutation before it ships.\n",{"id":145,"difficulty":14,"q":146,"a":147},"prefer-immutable","When should you prefer immutable updates over mutation?","Prefer **immutable** updates whenever the array is **shared** — across\ncomponents, stored in framework state, or passed between modules — because new\nreferences make change tracking and reasoning reliable.\n\n```js\n\u002F\u002F shared state -> immutable\nsetItems(items.filter(i => i.id !== id)) \u002F\u002F\n```\n\n**Mutation is fine** for short-lived local arrays you fully own inside a\nfunction, where in-place edits are faster and no one else can observe them.\n",null,{"description":11},"JavaScript array mutation interview questions — which methods mutate versus return a new array, immutability patterns, copying arrays, the new toSorted and with methods, and structuredClone for nested data.","javascript\u002Farrays\u002Fmutating-vs-nonmutating","Mutating vs Non-Mutating","Arrays & Iteration","arrays","2026-06-18","54QuTuRi8ysB-Ri5IyNJ6VfxTJXeWyfqI2yjBC5sMQg",[158,162,163,167],{"subtopic":159,"path":160,"order":161},"Array Methods","\u002Fjavascript\u002Farrays\u002Farray-methods",1,{"subtopic":152,"path":20,"order":12},{"subtopic":164,"path":165,"order":166},"Searching & Sorting","\u002Fjavascript\u002Farrays\u002Fsearching-sorting",3,{"subtopic":168,"path":169,"order":170},"Array Destructuring & Spread","\u002Fjavascript\u002Farrays\u002Fdestructuring-spread",4,{"path":172,"title":173},"\u002Fblog\u002Fjavascript-mutating-vs-nonmutating-arrays","Mutating vs Non-Mutating Array Methods in JavaScript — Immutability Done Right",1781808676038]