はじめに
JavaScriptはその歴史経緯から「それ言語の欠陥だろ!」的な問題が大きい言語だと思います。
個人的にほとんどのアプリケーション開発で問題となるような言語仕様とlodash(簡単だからね!)による回避策をまとめます。
※ 注意
自分の浅い経験のみから導いた記事だったためコメントにて補足していただきました。
JavaScriptはオブジェクト指向言語であるため、そのオブジェクトは参照を通して扱われるのは普通です。
オブジェクト型が等しいことの判定
問題点
オブジェクト型(配列とオブジェクト)の中身が等しいかを判定したいとします。
===
で判定しようとしてもうまくいきません。
const a = [1, 2, 3]
const b = [1, 2, 3]
console.log(a === b) // false
const c = {name: 'Tom', job: 'Engineer'}
const d = {name: 'Tom', job: 'Engineer'}
console.log(c === d) // false
上記の場合、参照するメモリが同じかどうかを比較しているため中身が同じでも参照するメモリが異なる場合はfalseとなります。
解決方法
lodashを使うことで解決できます。
自作でも他のプラグインでも問題ないですが個人的には簡単で実績のあるlodashを使ってます。
npmでlodashをインストールした上で、
import _ from lodash
const a = [1, 2, 3]
const b = [1, 2, 3]
console.log(_.isEqual(a, b)) // true
const c = {name: 'Tom', job: 'Engineer'}
const d = {name: 'Tom', job: 'Engineer'}
console.log(_.isEqual(c, d)) // true
isEqual
メソッドを使うことでオブジェクト型の中身の比較をすることが出来ました。
オブジェクト型のコピー
問題点
コピーをしたい時に単純に =
を使ってしまうと同じメモリを参照するため、コピー先を変更するとコピー元も更新されてしまう。いわゆる参照渡しの問題がある。
そのため、オブジェクト型をコピーする時はObject.assign()
を使う。こうすることで値渡しになる。
const defaultValue = {
value: 'この値は書き換えたくない'
}
const newValue = defaultValue
newValue.value = '新しい値'
console.log(defaultValue) // { "value": "新しい値" }
console.log(newValue) // { "value": "新しい値" }
const defaultValue = {
value: 'この値は書き換えたくない'
}
const newValue = Object.assign({}, defaultValue)
newValue.value = '新しい値'
console.log(defaultValue) // { "value": "この値は書き換えたくない" }
console.log(newValue) // { "value": "新しい値" }
ただし、Object.assign()
を使うとオブジェクトがネストするような場合にネストしたオブジェクトが参照渡しになってしまうという問題がある。個人的にはこれは謎仕様である。
const defaultValue = {
value: 'この値は書き換えたくない',
nestValue: {
value: 'この値は書き換えたくない'
}
}
const newValue = Object.assign({}, defaultValue)
newValue.nestValue.value = '新しい値'
newValue.value = '新しい値'
console.log(defaultValue) // { "value": "この値は書き換えたくない", "nestValue": { "value": "新しい値" } }
console.log(newValue) // { "value": "新しい値", "nestValue": { "value": "新しい値" } }
Object.assign()
を使ってもコピー元の中のオブジェクトの値が書き換わってしまう。
解決方法
この問題もlodashが解決してくれる。
npmでlodashをインストールした上で、
const defaultValue = {
value: 'この値は書き換えたくない',
nestValue: {
value: 'この値は書き換えたくない'
}
}
const newValue = _.cloneDeep(defaultValue)
newValue.nestValue.value = '新しい値'
newValue.value = '新しい値'
console.log(defaultValue) // { "value": "この値は書き換えたくない", "nestValue": { "value": "この値は書き換えたくない" } }
console.log(newValue) // { "value": "新しい値", "nestValue": { "value": "新しい値" } }
cloneDeep()
を使うことで、コピー元を残したままオブジェクト型のコピーが出来る。
結論
lodashを使おう!