JavaScriptにもJavaやC#と同じように参照型変数が存在し、オブジェクトや配列、関数が参照型変数です。
そして、JavaやC#と同じように、単純に「=」で代入するだけでは参照先(オブジェクトのメモリ領域を示すポインタ)しかコピーできず、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。
(このようなコピーを「シャローコピー(浅いコピー)」と呼びます)
これを避けたい場合には、参照しているメモリの中身を丸ごとコピー(新たにメモリ領域を確保し書き込み)する必要があります。 (このようなコピーを「ディープコピー(深いコピー)」と呼びます)
JavaScriptでは標準でディープコピーを行うための関数が用意されていないので、自力で実装するか、外部のライブラリを使用するかする必要があります。
今回は、deepcopyというライブラリを使用します。
(なお、ディープコピーをサポートするライブラリは他にもあり、用途に応じて使い分けた方が良いです。JSONでシリアライズ・デシリアライズする方法もありますが、関数やundefinedがコピーされない等の問題があるので注意が必要です。)
以下、サンプルコードです。
Node.jsを使用して検証します。
【事前準備】
・Node.js command prompt を起動
・作業フォルダ(今回は”C:\tmp\”)に移動
1 |
cd c:\tmp\ |
・deepcopyライブラリをインストール
1 |
npm install deepcopy |
【サンプルコード】
サンプルコードは作業フォルダ直下に作成します。
・ShallowCopy.js
1 2 3 4 5 6 |
let item1 = {id:1, name:"Sword"}; let item2 = item1; item2.id = 2; // オブジェクトのコピー item2.name = "Shield"; console.log(item1); console.log(item2); |
・DeepCopy.js
1 2 3 4 5 6 7 |
const deepcopy = require('deepcopy'); // コピー用ライブラリ読込 let item1 = {id:1, name:"Sword"}; let item2 = deepcopy(item1); // オブジェクトのコピー item2.id = 2; item2.name = "Shield"; console.log(item1); console.log(item2); |
【実行結果】
1 2 3 4 5 6 7 8 9 |
c:\tmp>node ShallowCopy.js { id: 2, name: 'Shield' } { id: 2, name: 'Shield' } c:\tmp>node DeepCopy.js { id: 1, name: 'Sword' } { id: 2, name: 'Shield' } c:\tmp> |
いかがでしたでしょうか?
JavaScriptのディープコピーは実務でも誤りやすいポイントであり、関数が含まれるオブジェクトをJSONのシリアライズ・デシリアライズでディープコピーしてしまいバグになる所を実際に目にしたこともあります。
ディープコピーの方法は今回紹介した方法以外にも色々あるので、現場毎で確認すると良いでしょう。
コメント