JavaScript の配列は参照なので、破壊的な変更を加えてしまうと副作用を発生させるので取り扱いには注意 (危機管理) が必要です。
副作用を発生させないために配列操作を行う時はコピーを行うことが多いですが、配列が入れ子だったり、配列の中にオブジェクトを入れたりということも多いと思うので、改めて浅いコピー (shallow copy) と深いコピー (deep copy) について試したのでメモ
配列は参照
別の変数に代入してもコピーされるわけではない (同じデータを参照しているだけ)
const array = [ {name: 'Shamiko'}, {name: 'Chiyoda Momo'}, ]; const copyArray = array; // 配列を代入した変数と元の配列は同一 (同じデータを参照している) console.log(copyArray === array); // => true copyArray.push({name: 'Hinatsuki Mikan'}); copyArray[0].name = 'Ririsu'; console.log(copyArray); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, {name: 'Hinatsuki Mikan'}] console.log(array); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, {name: 'Hinatsuki Mikan'}]
slice() でコピー
const array = [ {name: 'Shamiko'}, {name: 'Chiyoda Momo'}, ]; const copyArray = array.slice(); // コピーされているので内容は同じでも同一ではなくなっている console.log(copyArray === array); // => false copyArray.push({name: 'Hinatsuki Mikan'}); copyArray[0].name = 'Ririsu'; console.log(copyArray); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, {name: 'Hinatsuki Mikan'}] console.log(array); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}]
Array.slice()
は shallow copy
ref. Array.prototype.slice() - JavaScript | MDN
スプレッド構文 ( […Array]
) でコピー
const array = [ {name: 'Shamiko'}, {name: 'Chiyoda Momo'}, ]; const copyArray = [...array]; console.log(copyArray === array); // => false copyArray.push({name: 'Hinatsuki Mikan'}); copyArray[0].name = 'Ririsu'; console.log(copyArray); // =>[{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, {name: 'Hinatsuki Mikan'}] console.log(array); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}]
スプレッド構文 [...Array]
は shallow copy
ref.
Array.from() でコピー
const array = [ {name: 'Shamiko'}, {name: 'Chiyoda Momo'}, ]; const copyArray = Array.from(array); console.log(copyArray === array); // => false copyArray.push({name: 'Hinatsuki Mikan'}); copyArray[0].name = 'Ririsu'; console.log(copyArray); // =>[{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, {name: 'Hinatsuki Mikan'}] console.log(array); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}]
Array.from()
は shallow copy
ref. Array.from() - JavaScript | MDN
Object.assign() でコピー
const array = [ {name: 'Shamiko'}, {name: 'Chiyoda Momo'}, ]; const copyArray = Object.assign([], array); console.log(copyArray === array); // => false copyArray.push({name: 'Hinatsuki Mikan'}); copyArray[0].name = 'Ririsu'; console.log(copyArray); // =>[{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, {name: 'Hinatsuki Mikan'}] console.log(array); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}]
Object.assign()
は shallow copy
ref. Object.assign() - JavaScript | MDN
JSON.stringify() で文字列に変換してコピー
JSON.stringify()
で文字列に変換して、JSON.parse()
で再び配列に戻す方法
const array = [ {name: 'Shamiko'}, {name: 'Chiyoda Momo'}, ]; const copyArray = JSON.parse(JSON.stringify(array)); console.log(copyArray === array); // => false copyArray.push({name: 'Hinatsuki Mikan'}); copyArray[0].name = 'Ririsu'; console.log(copyArray); // =>[{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, {name: 'Hinatsuki Mikan'}] console.log(array); // => [{name: 'Shamiko'}, {name: 'Chiyoda Momo'}]
JSON.stringify()
で一度文字列にしてしまえば、deep copy ができる
※ 但し、JSON.stringify()でのコピーは、Date オブジェクトや function など 文字列化すると壊れるオブジェクトが含まれていると正しくコピーできない
const array = [ new Date(), () => { console.log('function') }, ]; const copyArray = JSON.parse(JSON.stringify(array)); console.log(copyArray); // => [ '2019-11-29T10:47:09.941Z', null ] console.log(array); // => [ 2019-11-29T10:47:09.941Z, [Function] ]
文字列化した際に、オブジェクトが壊れてしまう
lodash#cloneDeep でコピー
lodash ライブラリに含まれる cloneDeep メソッドを使ってコピー
const _ = require('lodash'); const array = [ {name: 'Shamiko'}, {name: 'Chiyoda Momo'}, new Date(), {func() {console.log('foo');} ]; const copyArray = _.cloneDeep(array); // => false copyArray.push({name: 'Hinatsuki Mikan'}); copyArray[0].name = 'Ririsu'; console.log(copyArray); // => [{name: 'Ririsu'}, {name: 'Chiyoda Momo'}, 2019-11-29T10:57:00.595Z, {func: [Function: func]}, {name: 'Hinatsuki Mikan'}] console.log(arg); // => [{name: 'Shamiko'}, {name: 'Chiyoda Momo'}, 2019-11-29T10:57:00.595Z, {func: [Function: func]}] copyArray[2].getFullYear(); // => 2019 copyArray[3].func(); // => "foo"
lodash#cloneDeep
は deep copy で、オブジェクトもコピーされる
まとめ
- JavaScriptの配列をコピーできる方法は、ほぼ shallow copy なので、入れ子になっているデータの取り扱いには注意 (危機管理) が必要
- 文字列化できるデータしか入ってないことが確実な場合は、
JSON.parse(JSON.stringify(Array))
で deep copy することが可能 - function などが含まれる配列を deep copy したい場合は、再帰などを使った独自の関数を作る必要がある
- ライブラリを使えるなら deep copy は
lodash#cloneDeep
を使うのが簡単
JavaScript たのしい!
[参考]
- JavaScript Deep copy for array and object - Gamshan Vigneswaran - Medium
- [JavaScript]色々なディープコピー - Qiita
- 作者: Mario Casciaro,Luciano Mammino,武舎広幸,阿部和也
- 出版社/メーカー: オライリージャパン
- 発売日: 2019/05/18
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
- 作者: 伊藤いづも
- 出版社/メーカー: 芳文社
- 発売日: 2017/09/27
- メディア: Kindle版
- この商品を含むブログを見る
まちカドまぞくの3巻まじ尊さの塊なのでシャミ子の二期お願いしますッ