回答編集履歴
1
コピーメソッドを、クラスメソッドもコピーできるものに修正、参照についての説明に補足追記
answer
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
[[JavaScript]色々なディープコピー - Qiita](https://qiita.com/knhr__/items/d7de463bf9013d5d3dc0)
|
2
2
|
|
3
3
|
を参考に、ディープコピーメソッドを作った上で、
|
4
|
+
(上記を発展させ、クラスメソッド(prototype定義のメソッドも)含めてコピーできるようにしている)
|
4
5
|
下記でいかがでしょうか。
|
5
6
|
|
6
7
|
```javascript
|
@@ -30,13 +31,15 @@
|
|
30
31
|
|
31
32
|
// ディープコピー関数を定義
|
32
33
|
const deepClone = obj => {
|
33
|
-
|
34
|
+
const r = (() => {
|
35
|
+
// 一応、余計な処理をいれないために、クラス名がObjectか、Arrayだったらそのまま、配列かオブジェクトかを返す
|
36
|
+
if (obj.constructor.name === 'Array' || obj.constructor.name === 'Object') return Array.isArray(obj) : [] : {};
|
37
|
+
// クラス名が上記以外なら、(クラス、prototypeがユーザー定義)Object.createをうまく使って、新しくインスタンスを生成して返す
|
38
|
+
return Object.create(obj.constructor.prototype);
|
39
|
+
})();
|
34
|
-
for(
|
40
|
+
for(let key in obj){
|
41
|
+
// プロパティがオブジェクトなら、再起的に自身を実行して潜っていく、値なら、そのまま代入
|
35
|
-
|
42
|
+
r[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
|
36
|
-
r[name] = deepClone(obj[name])
|
37
|
-
} else {
|
38
|
-
r[name] = obj[name]
|
39
|
-
}
|
40
43
|
}
|
41
44
|
return r;
|
42
45
|
};
|
@@ -99,9 +102,10 @@
|
|
99
102
|
```
|
100
103
|
|
101
104
|
maisumakunさんがおっしゃるとおり、Object.createは、ディープコピーを生成するものではないので、改めて、ディープコピーをさせるメソッドを用意する必要があるのでしょう。
|
105
|
+
(Object.createは、第一引数のオブジェクトを、"prototype"として、新たにオブジェクトを生成するので、コピーメソッドの中でうまく使えば、完全に同クラスからの別参照インスタンスオブジェクトとなる)
|
102
106
|
|
103
107
|
さて、余談になりますが、**JavaScriptには、値渡しも参照渡しも存在しません**。
|
104
|
-
あくまで、変数は、**メモリに格納された値に対する参照**にすぎません。
|
108
|
+
あくまで、変数は、**メモリに格納された値、またはオブジェクトに対する参照**にすぎません。
|
105
109
|
```javascript
|
106
110
|
const a = {value: 15};
|
107
111
|
const b = a;
|
@@ -113,14 +117,39 @@
|
|
113
117
|
`a`を代入された`b`は、`{value: 15}`というオブジェクトに対する参照となり、
|
114
118
|
その時点で、`a`と`b`の関係は代入した時点で断たれています。
|
115
119
|
ただし、**メモリに格納された同じオブジェクト自体を参照**しているので、`b`によって、プロパティの値を変えると、同じオブジェクトを参照している`a`の方も変化があったように見えます。
|
120
|
+
(各プロパティもまた、値に対する参照のため。オブジェクトは、参照をプロパティとして複数保持できるもの)
|
116
121
|
よって、下記の場合は、`a`の変化を`b`でさせることはできなくなります。
|
117
122
|
|
118
123
|
```javascript
|
119
124
|
const a = {value: 15};
|
120
|
-
|
125
|
+
let b = a;
|
126
|
+
// bは、新たに生成されたオブジェクトの参照となるため、aとは参照元さえも変わってしまう
|
121
127
|
b = {value: 15};
|
122
128
|
b.value = 20;
|
123
129
|
console.log(a.value); // 15
|
124
130
|
```
|
125
131
|
|
132
|
+
オブジェクトと同様に、値に対しても同じ参照の挙動となります。
|
133
|
+
|
134
|
+
```javascript
|
135
|
+
const a = 15;
|
136
|
+
let b = a; // bは、同じ参照元の15という値の参照となる
|
137
|
+
b = 20; // bは、新たな値20の参照となる
|
138
|
+
|
139
|
+
console.log(a); // 15
|
140
|
+
```
|
141
|
+
|
142
|
+
```javascript
|
143
|
+
const a = 15;
|
144
|
+
let b = a; // bは、同じ参照元の15という値の参照となる
|
145
|
+
// だが、オブジェクトと違い、裸の値であるため、どう代入し直しても、新しい値が生成される。(上記の通り新しいオブジェクトの代入は新たにメモリに格納されるので、裸の値と参照の挙動自体は同じ)
|
146
|
+
b = 15; // bは、値的には同じでも、参照元が変わってしまう。
|
147
|
+
|
148
|
+
console.log(a); // 15
|
149
|
+
```
|
150
|
+
|
151
|
+
つまり、変数に対して、値を渡している、参照を渡しているのではなく、
|
152
|
+
常に変数は、**値(オブジェクト)に対しての参照そのもの**となります。
|
153
|
+
値が入っている、参照が入っている、それを渡している、ではないというわけです。
|
154
|
+
|
126
155
|
以上です。
|