📋

[JavaScript] オブジェクト操作をイミュータブルにする

2022/11/04に公開

前提

JavaScriptではconstで配列を使用してもあくまで参照元が変わらないことを保証しているだけで中身は変更できる。
MDNでは以下のように書かれている。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const

const 宣言は、値への読み取り専用の参照を作ります。これは、定数に保持されている値は不変ではなく、その変数の識別子が再代入できないということです。たとえば、定数の中身がオブジェクトの場合、オブジェクトの内容(プロパティなど)は変更可能です。

なのでオブジェクト(配列含む)は基本的にコピーしてから操作する
なお、プリミティブ型はもともとイミュータブルなので書き換えることはできない
forEach, map, filter, reduce ... は扱わない(These are 教養!)
ここでは可変な操作を不変で置き換える方法一覧を置いておく

Array

push()

const newArray = [...ary, elm1, elm2]

unshift()

const newArray = [elm1, elm2, ...ary]

pop()

const elm = ary[length-1]
const newArray = ary.slice(0, ary.length-1)

shift()

const elm = ary[0]
const newArray = ary.slice(1)

splice()

const newArray = [...ary.slice(0, 1), ...[elm1, elm2], ...ary.slice(2)]

sort()

const newArray = [...ary].sort()

reverse()

const newArray = [...ary].reverse()

Object

Object.preventExtensions(): 追加 ×
Object.seal(): 追加、削除 ×
Object.freeze(): 追加、削除、変更 ×

プロパティ追加 obj.newKey = newValue

const newObj = {...obj, newKey1: newValue1, newKey2: newValue2}

既存のプロパティ変更 obj.existKey = newValue

const newObj = {...obj, existKey1: newValue1, existKey2: newValue2 }

プロパティの削除 delete obj.existKey

const { existKey1, existKey2, ...newObj} = obj

ネストした構造の場合

個別にスプレッド構文{...obj}を使った後に個別に変更

const newObj = {
  ...obj,
  existKey1: [...obj.existKey1],
  existKey2: {...obj.existKey2}
}
// ↓元のobjは変わらない
newObj.existKey1.push('newValue') 
newObj.existKey2.childkey = 'newValue'

{...JSON.parse(JSON.stringify(ary))}をした後に個別に変更

const newObj = {
   ...JSON.parse(JSON.stringify(obj)),
}
// ↓元のobjは変わらない
newObj.existKey1.push('newValue') 
newObj.existKey2.childkey = 'newValue'

参考URL

https://morioh.com/p/1c11f60f6585
https://iwb.jp/javascript-const-array-variable-not-change/
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const

Discussion