ヒープとスタック
JavaScriptの実行時には、主にコード空間、スタック空間、ヒープ空間の3種類のメモリ空間が存在します。コード空間は主に実行可能なコードを格納するために使用され、スタックとヒープはデータを格納するために使用されます。
プリミティブデータの値は「スタック」に直接格納され、参照値は「ヒープ」に格納されます。一般的に、スタック領域はあまり大きく設定されず、主にプリミティブ型の小さなデータを格納するために使用されます。参照型のデータはより多くのスペースを取るので、このタイプのデータはヒープに格納されますが、ヒープスペースは非常に大きく、大きなデータをたくさん格納することができます。しかし、メモリの確保と再利用に時間がかかるという欠点があります。
JavaScriptでは、代入操作は他の言語とは大きく異なり、プリミティブ型の代入は変数値をそのままコピーし、参照型の代入は参照アドレスをコピーします。 プリミティブ型のデータはスタックに、参照型のデータはヒープに格納されます。ヒープ内のデータは参照によって変数に関連付けられます。
以下に例を示します。
function foo(){
var a = "ant"
var b = a
var c = { name : "ant" }
var d = c
}
foo()
メモリには、下図のようなエントリが保存されます:
cとdは同じ参照で、同じアドレスを指しており、どちらかがnameの属性を変更すると、もう一方もそれに応じて変更されます。
ディープコピーとシャローコピーの違い
浅いコピー:メモリ上にオブジェクトのコピーを作成し、メモリ上に新しい空間を作成します。 コピーされたオブジェクトのプロパティが基本データ型の場合、コピーは値そのものになり、複雑なデータ型の場合、コピーはアドレスになるため、新しいオブジェクトを変更すると元のオブジェクトに影響を与えます。
ディープコピー:複雑なデータ型を含む新しい空間、完全なコピーを開き、オブジェクトのコピーと元のオブジェクトは、任意の関係を持っていない、何の変更は、互いに影響されません。
ディープ・コピーの実装
基本的なディープコピーコードを書くことから始めましょう:
function deepClone(obj) {
if (typeof(obj) !=="object") {
return obj
} else {
let newObj = {}
for (let key in obj) {
newObj[key] = deepClone(obj[key])
}
return newObj
}
}
このコードは基本的なディープコピーを実装していますが、objがNULL、Date型、RegExp型、配列などの特殊なケースを解決できません。そこで、これらの特殊なケースを解決するためにコードを少し変更しました。
function deepClone(obj) {
if (obj === null) return null //null
if (obj instanceof RegExp) return new RegExp(obj) //RegExp
if (obj instanceof Date) return new Date(obj) //Date
if (typeof(obj) !=="object") {
return obj
} else {
let newObj = new obj.__proto__.constructor //Arrayまたはオブジェクト
for (let key in obj) {
newObj[key] = deepClone(obj[key])
}
return newObj
}
}
テストしてください:
let src = {
name: "ant",
date: new Date(),
reg: /^666$/,
arr: ["a", "n", "t"],
info: {
age: 999,
job: "developer"
}
}
let target = deepClone(src)
console.log(target)
結果





