Mutable vs Immutable Objects in Javascript
Even outside of functional programming, Javascript provides mutable and immutable functions and methods to process data. Which one you use depends on the context of the application you're working on. In this post, we'll explore the differences between mutable and immutable objects in Javascript, and how to use them. ## Definitions **Mutable objects** : Objects whose state can be modified after they are created : * In Javascript, most objects are mutable by default. This means you can change their properties, add new properties, or remove existing ones without creating a new object : * This is what most people think of when they hear the word "object" in Javascript **Immutable objects** : Objects whose state cannot be modified after they are created : * In Javascript, strings and numbers are immutable primitives : * When you perform operations on them, you create new values rather than changing the original ones. Understanding the differences between mutable and immutable objects is crucial for writing efficient and predictable code in Javascript. ## Array methods Javascript provides a variety of methods for manipulating arrays. Some of these methods are mutable, meaning they change the original array, while others are immutable, meaning they return a new array without modifying the original. ### Mutable Array Methods These mutable array methods modify the original array, so if you need to access the original array later, you won't be able to, unless you've made a copy of it before using these methods. For example, if you use the [forEach()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) method to iterate over an array and modify its elements, the original array will be changed. ```typescript let numbers: number[] = [1, 2, 3, 4]; numbers.forEach(num => console.log(num * num)); ``` The methods below are mutable and will change the original array. | Method | Example | Description | |:---: | :---: | :---: | | [push()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) | arr.push(1) | Add to end | | [pop()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop) | arr.pop() | Remove from end | | [shift()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) | arr.shift() | Remove from start | | [unshift()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift) | arr.unshift(1) | Add to start | | [splice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) | arr.splice(1, 2, 9) | Insert/delete elements | | [sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) | arr.sort() | Sorts array in-place | | [reverse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) | arr.reverse() | Reverses array in-place | | [copyWithin()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) | arr.copyWithin(0, 2) | Copy part of array within itself | | [fill()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill) | arr.fill(0) | Fill array with static value | ### Immutable Array Methods These method return a new array without changing the original and the original array will remain unchanged. This is a good practice to follow when working with shared or global state. These methods are also side-effect free, meaning they don't interact with the outside world or change state outside their scope. This makes them easier to reason about and test. When using the [map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) method to iterate over an array, you create a new array with the results of applying a function to each element of the original array without modifying the original array. ```typescript // Declare the type of the array explicitly const numbers: number[] = [1, 2, 3, 4] // Use map to double each number const doubled: number[] = numbers.map((num: number): number => num * 2) console.log(doubled) // Output: [2, 4, 6, 8] console.log(numbers) // Output: [1, 2, 3, 4] ``` These methods are immutable and will not change the original array. | Method | Example | Description | | :---: | :---: | --- | | [map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) | arr.map(x => x * 2) | Transform values | | [filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) | arr.filter(x => x > 1) | Filter elements | | [slice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) | arr.slice(0, 2) | Return sub-array | | [concat()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) | arr.concat([4, 5]) | Merge arrays | | [flat()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) | arr.flat(1) | Flatten nested arrays | | [Spread](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) | [...arr] | Clone array | | [Array.from()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) | Array.from(arr) | Clone array | ES2023 introduces new immutable methods for manipulating arrays. These methods return a new array with the changes, promoting immutability. The new methods are: | Method | Example | Description | |:---: | :---: | --- | | [toSorted()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted) | arr.toSorted() | Sorted copy (ES2023) | | [toReversed()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toReversed) | arr.toReversed() | Reversed copy (ES2023) | | [toSpliced()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced) | arr.toSpliced(1, 1, 99) | Spliced copy (ES2023) | | [with()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/with) | arr.with(1, 100) | Creates a new array with the element at the specified index replaced with the provided value.| | [findLast()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast) | arr.findLast(x => x > 2) | Returns the value of the last element in the array that satisfies a provided testing function.| | [findLastIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex) | arr.findLastIndex(x => x > 2) | Returns the index of the last element in the array that satisfies a provided testing function.| | [group()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/group) | arr.group(x => x % 2) | Groups the elements of an array based on the result of a callback function.| | [groupToMap()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/groupToMap) | arr.groupToMap(x => x % 2) | Groups the elements of an array into a Map based on the result of a callback function.| The code below demonstrates how to use these new methods. We will use a single `originalArray` and modify it using these new methods.`. Two notes: All methods were introduced in ES2023 and require `"target": "ES2023"` in `tsconfig.json` when working with Typescript. `group()` returns a [Record