インターネット上のほとんどのソリューションにはいくつかの問題があります。そこで、私はフォローアップを行うことにしました。これには、受け入れられた回答を受け入れない理由が含まれています。
開始状況
Javascriptのすべての子とその子などをディープコピーしたいObject
。しかし、私は普通の開発者ではないので、私Object
は普通の properties
、circular structures
そしてさえ持っていnested objects
ます。
ではcircular structure
、nested object
最初のとを作成しましょう。
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Object
名前付きですべてをまとめましょうa
。
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
次に、a
という名前の変数にコピーして、b
それを変更します。
var b = a;
b.x = 'b';
b.nested.y = 'b';
あなたはここで何が起こったのか知っています。
console.log(a, b);
a --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
次に、解決策を見つけましょう。
JSON
私が最初に試みたのはを使用することJSON
でした。
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
時間を無駄にしないでくださいTypeError: Converting circular structure to JSON
。
再帰コピー(受け入れられた「答え」)
受け入れられた答えを見てみましょう。
function cloneSO(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = cloneSO(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
いいですね これはオブジェクトの再帰的なコピーであり、などの他のタイプも処理しますDate
が、これは必須ではありませんでした。
var b = cloneSO(a);
b.x = 'b';
b.nested.y = 'b';
再帰とcircular structures
一緒にうまく動作しません...RangeError: Maximum call stack size exceeded
ネイティブソリューション
同僚と議論した後、上司は何が起こったのか私たちに尋ねました、そして彼はいくつかのグーグルの後で簡単な解決策を見つけました。と呼ばれていObject.create
ます。
var b = Object.create(a);
b.x = 'b';
b.nested.y = 'b';
このソリューションは少し前にJavaScriptに追加され、処理さえしましたcircular structure
。
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
...そして、ご覧のとおり、ネストされた構造の内部では機能しませんでした。
ネイティブソリューションのポリフィル
Object.create
IE 8と同じように、古いブラウザーにはポリフィルがあります。これは、Mozillaが推奨するようなものです。もちろん、完璧ではなく、ネイティブソリューションと同じ問題が発生します。
function F() {};
function clonePF(o) {
F.prototype = o;
return new F();
}
var b = clonePF(a);
b.x = 'b';
b.nested.y = 'b';
私はF
範囲の外に置いたので、何instanceof
が私たちに伝えているかを見ることができます。
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> F {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> true
ネイティブソリューションと同じ問題ですが、出力は少し悪いです。
より良い(しかし完璧ではない)ソリューション
調べてみると、これに似た質問(JavaScriptでは、ディープコピーを実行するときに、プロパティが「これ」であるためにサイクルを回避するにはどうすればよいですか?)
function cloneDR(o) {
const gdcc = "__getDeepCircularCopy__";
if (o !== Object(o)) {
return o; // primitive value
}
var set = gdcc in o,
cache = o[gdcc],
result;
if (set && typeof cache == "function") {
return cache();
}
// else
o[gdcc] = function() { return result; }; // overwrite
if (o instanceof Array) {
result = [];
for (var i=0; i<o.length; i++) {
result[i] = cloneDR(o[i]);
}
} else {
result = {};
for (var prop in o)
if (prop != gdcc)
result[prop] = cloneDR(o[prop]);
else if (set)
result[prop] = cloneDR(cache);
}
if (set) {
o[gdcc] = cache; // reset
} else {
delete o[gdcc]; // unset again
}
return result;
}
var b = cloneDR(a);
b.x = 'b';
b.nested.y = 'b';
そして、出力を見てみましょう...
console.log(a, b);
a --> Object {
x: "a",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "a"
}
}
b --> Object {
x: "b",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> false
要件は一致しているが、変更など、いくつかの小さな問題、まだそこにあるinstance
のnested
とcirc
にはObject
。
葉を共有する木の構造はコピーされず、2つの独立した葉になります。
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
結論
再帰とキャッシュを使用する最後のソリューションは最善ではないかもしれませんが、それはオブジェクトの実際のディープコピーです。これは、単純に処理しproperties
、circular structures
そしてnested object
クローニングながら、それは混乱意志それらのインスタンスをアップ。
jsfiddle