JavaScriptでは、オブジェクトと配列は参照渡しで行われます。まず、この概念について簡単に説明します。
参照渡し
引用型は、総称として Object 型と呼ばれます。細分すると、Object 型、Array 型、Date 型、RegExp 型、Function 型などがあります。
引用型の値は参照によってアクセスされます
参照オブジェクトの保存方法は、スタックに変数名を保存し(その変数の値はヒープにあるデータのポインタ、つまりオブジェクトがヒープに保存されているアドレスです)、ヒープにデータそのものを保存します。
オブジェクトは参照渡しを使用します。あるオブジェクトを新しい変数に代入すると、そのオブジェクトがヒープに保存されているアドレスが代入され、ヒープのデータそのものではありません。つまり、2つのオブジェクトは同じ記憶空間を指しており、どちらかのオブジェクトが変更されると、実際には記憶空間の内容が変更されるため、2つのオブジェクトは連動します。
コード例
//ケース1
var a = [1, 2, 3];
var b = a;
a = [4, 5, 6];
console.log(b); // [1, 2, 3]
上記のコードでは、配列は基本データ型のように見えますが、実際には以下のように動作します:
//ケース2
var a = [1, 2, 3];
var b = a;
a.pop();
console.log(b); // [1, 2]
これはなぜでしょうか?
a = [4, 5, 6]; // これはa参照そのものを変更しており、配列オブジェクトを変更していません。
a.pop(); // これは配列オブジェクトを変更しており、a参照は変更されません。
b = a; // この操作により、bは配列オブジェクトを直接指すようになります。bがaを指し、aが配列を指すわけではありません。
// そのため、a参照を変更してもb参照には影響がなく、配列オブジェクトを変更すると影響があります。
上記コードの図示
解決策
1. JSON文字列に変換してからオブジェクトまたは配列に戻す
JSON.parse(JSON.stringify(...))
var a = [1, 2, 3];
var b = JSON.parse(JSON.stringify(a));
a.pop();
console.log(b); // [1, 2, 3]
2. Lodashライブラリを使用
Lodashには、元のデータを再帰的にディープコピーする _.cloneDeep(value)
関数があります。詳細はこちらをご覧ください。
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]); // => false
const _ = require('lodash');
var a = [1, 2, 3];
var b = _.cloneDeep(a);
a.pop();
console.log(b); // [1, 2, 3]
長文をお読みいただき、ありがとうございました。