js深コピーと浅いコピー
4731 ワード
javaScriptの変数タイプ基本タイプ:5種類の基本データタイプUdefined、Null、Boolean、Number、String、変数は直接値によって保存され、スタックメモリに保存された簡単なデータセグメントで直接アクセスできます. 参照タイプ:ヒープメモリに格納されているオブジェクトは、変数が保存されているポインタであり、このポインタは別の位置を指しています.参照タイプ(オブジェクト、配列など)の値にアクセスする必要がある場合は、まずスタックからそのオブジェクトのアドレスポインタを取得し、次にスタックメモリから必要なデータを取得します. JavaScriptの記憶オブジェクトはいずれもアドレスを保存しているので、浅いコピーは
浅いコピーの実現簡単な引用コピー Object.assign()は、第一層変数に対してのみ深くコピーされます. Arayのsliceおよびconcat方法(Object.assignと同じ) 元の配列の要素は下記の規則に従ってコピーされます.要素がオブジェクト参照(実際のオブジェクトではない)である場合、 は、文字列、数字およびブール値( は、2つの配列のいずれかに新しい要素を追加すると、もう1つは影響を受けない. 深くコピーした実装
JSON対象のパーとストリングス
もう一つの悪いところは相手を捨てることができる
オブジェクトに循環参照がある場合も正確に処理できません.
カスタム方法で深コピーを実現
obj1
とobj2
と同じブロックのメモリアドレスを指すことになる.どちらかの内容が変更されました.元のメモリで修正するとコピー対象とソースオブジェクトが変更されます.深度コピーは新しいメモリアドレスを開いて、元のオブジェクトの各属性を一つずつコピーします.コピー対象とソースオブジェクトのそれぞれの動作には影響がありません.浅いコピーの実現
var data = {a: 1, b: 2};
var c = data;
var data = {
a: 1,
b: { f: { g: 1 } },
c: [ 1, 2, 3 ]
};
var objData = Object.assign({}, data);
console.log(objData === data); //false
console.log(objData.b.f === data.b.f); //true
Array
のslice
およびconcat
方法は、元の配列を修正せずに、元の配列の要素を浅いコピーした新しい配列だけを返す.深くコピーしたように見えますが、実は浅いコピーです.var array = [1,2,3];
var array_shallow = array;
var array_concat = array.concat();
var array_slice = array.slice(0);
console.log(array === array_shallow); //true
console.log(array === array_slice); //false,“ ”
console.log(array === array_concat); //false,“ ”
は、直接参照コピーとは異なるconcat
およびslice
によって返される異なる配列例を示している.slice
は、このオブジェクトをコピーして新しい配列に参照する.両方のオブジェクトの参照は同じオブジェクトを参照しています.参照されているオブジェクトが変化すると、新しいものと元の配列のこの要素も変化します. var array = [1, [1,2,3], {name:"array"}];
var array_concat = array.concat();
var array_slice = array.slice(0);
array_concat[1][0] = 5; // array_concat
console.log(array[1]); //[5,2,3]
console.log(array_slice[1]); //[5,2,3]
array_slice[2].name = "array_slice"; // array_slice
console.log(array[2].name); //array_slice
console.log(array_concat[2].name); //array_slice
によれば、Arayのconcat
およびslice
は、本当に深い複製ではなく、配列中のオブジェクト要素(Object
、Array
など)は、参照のみをコピーしているだけであることがわかる.String
、Number
またはBoolean
オブジェクトではない)に対して、これらの値を新しい配列にコピーする.他の配列でこれらの文字列または数字またはブール値を変更すると、他の配列に影響を与えません.var array = [1, 2, 3];
var array_shallow = array;
var array_concat = array.concat();
var array_slice = array.slice(0);
array.push(4);
console.log(array_concat); //[1, 2, 3]
JSON対象のパーとストリングス
slice
オブジェクトはJSON
に導入された新しいタイプ(サポートされているブラウザはIE 8+)であり、ES5
オブジェクトJSON
方法はparse
文字列をJSON
オブジェクトに逆シーケンス化することができ、JS
方法はstringify
オブジェクトシーケンスをJS
文字列に変換することができ、これらの2つの方法によって、オブジェクトの深度コピーを実現することもできる.var source = { name:"source", child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
target.name = "target"; // target name
console.log(source.name); //source
console.log(target.name); //target
target.child.name = "target child"; // target child
console.log(source.child.name); //child
console.log(target.child.name); //target child
var source = { name:function(){console.log(1);}, child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
var source = { name:function(){console.log(1);}, child:new RegExp("e") }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
console.log(target.child); //Object {}
この方法は比較的簡単で、基本的な深度コピー要求を満たすことができ、JSON
フォーマットで表現できるすべてのデータタイプを処理することができるが、正規表現タイプ、関数タイプなどに対しては、深くコピーすることができない(しかも、直接に対応する値を失う).もう一つの悪いところは相手を捨てることができる
JSON
です.つまり、深くコピーした後、このオブジェクトの元の構造関数が何であれ、深くコピーした後にconstructor
になります.オブジェクトに循環参照がある場合も正確に処理できません.
カスタム方法で深コピーを実現
function clone(data) {
if (Array.isArray(data)) {
let newArr = [];
for (let i = 0; i < data.length; i++) {
newArr[i] = clone(data[i]);
}
return newArr;
} else if (data instanceof Object) {
let obj = {};
for (let key in data) {
obj[key] = clone(data[[key]]);
}
return obj;
}else {
return data;
}
}
var data = {
a: 1,
b: { f: { g: 1 } },
c: [ 1, 2, 3 ]
};
var objData = clone(data);
console.log(objData === data); //false
console.log(objdata.b.f === data.b.f); //false