🐥
JavaScript: 浅いコピーと深いコピー
JavaScriptにおける浅いコピー(Shallow copy)と深いコピー(Deep copy)について
参考ドキュメント
MDN Shallow copy
MDN Deep copy
MDN JSON stringify
コードをGitHubに上げてます。ご自由にお試しください。
浅いコピー(Shallow copy)
浅いコピーを行うと1階層のみコピーし、2階層目以降は参照した値を生成します。
スプレッド構文で行うと下記のようになります。
const profile = ['jon', { hobby: ['game', 'running'] }]
// 浅いコピーを作成、1階層のみコピーされます。
const profileCopy = [...profile]
この時、コピー先のprofileCopyのhobby値を書き換えると、コピー元のprofileのhobby値も変わってしまいます。
これは2階層目以降のコピー元を参照しているからです。
言い換えると2階層目以降のコピー元のメモリを共有しているということです。
// コピー先のhobby値を書き換えてもコピー元の値も変わる
profileCopy[1].hobby[0] = 'reading'
console.log('\nprofileCopyのhobbyをreadingに書き換える:', profileCopy);
// [ 'jon', { hobby: [ 'reading', 'running' ] } ]
console.log('コピー元も値は変わる:', profile);
// [ 'jon', { hobby: [ 'reading', 'running' ] } ]
もちろんメモリを共有しているので、コピー元のprofileを書き換えても同様にコピー先のprofileCopyも書き変わります。
// コピー元のhobby値を書き換えるとコピー先も変わる
profile[1].hobby[0] = 'cooking'
console.log('\nprofileのhobbyをcookinに書き換える:', profile);
// [ 'jon', { hobby: [ 'cooking', 'running' ] } ]
console.log('コピー先も値は変わる:', profileCopy);
// [ 'jon', { hobby: [ 'cooking', 'running' ] } ]
1階層の値はコピーされている値なので、値を書き換えてもコピー元、コピー先共に影響を与えません。
つまり、1階層はメモリの共有は行われていません。
// コピー元のjonはコピーされてるので、書き換えてもコピー先は変わらない
profile[0] = 'mai'
console.log('\nprofileのjonを書き換える:', profile);
// [ 'mai', { hobby: [ 'cooking', 'running' ] } ]
console.log('jonは参照ではなくコピーされてるので\nコピー元を書き換えてもコピー先の値は変わらない:', profileCopy);
// [ 'jon', { hobby: [ 'cooking', 'running' ] } ]
深いコピー
深いコピーは全ての階層をコピーします。
メモリを共有することはありません。
JSON.stringifyを使った深いコピーのやり方は下記のようになります。
const menu = ['coffee', {salad: ['chikin salad', 'tomato salad']}]
// 深いコピーを作成、全ての階層がコピーされます
const menuCopy = JSON.parse(JSON.stringify(menu))
この時、コピー先のsaladを書き換えてもコピー元に影響はありません。
メモリを共有してないので、書き変わらないということです。
menuCopy[1].salad[0] = 'caesar salad'
console.log('\nmenuCopyのsaladを書き換える:', menuCopy);
// [ 'coffee', { salad: [ 'caesar salad', 'tomato salad' ] } ]
console.log('コピー元に影響はない:', menu)
// [ 'coffee', { salad: [ 'chikin salad', 'tomato salad' ] } ]
同様にコピー元のcoffeeを書き換えても、コピー先に影響はありません。
menu[0] = 'drink'
console.log('\nmenuのcoffeeを書き換える:', menu);
// [ 'drink', { salad: [ 'chikin salad', 'tomato salad' ] } ]
console.log('コピー先に影響はない:', menuCopy);
// [ 'coffee', { salad: [ 'caesar salad', 'tomato salad' ] } ]
Discussion