私はオブジェクトを持っていますx
。y
への変更が変更されy
ないように、オブジェクトとしてコピーしたいと思いますx
。組み込みの JavaScript オブジェクトから派生したオブジェクトをコピーすると、余分な不要なプロパティが作成されることに気付きました。独自のリテラル構成オブジェクトの 1 つをコピーしているので、これは問題ではありません。
JavaScript オブジェクトを正しく複製するにはどうすればよいですか?
私はオブジェクトを持っていますx
。y
への変更が変更されy
ないように、オブジェクトとしてコピーしたいと思いますx
。組み込みの JavaScript オブジェクトから派生したオブジェクトをコピーすると、余分な不要なプロパティが作成されることに気付きました。独自のリテラル構成オブジェクトの 1 つをコピーしているので、これは問題ではありません。
JavaScript オブジェクトを正しく複製するにはどうすればよいですか?
JavaScript の任意のオブジェクトに対してこれを行うことは、簡単でも簡単でもありません。オブジェクトのプロトタイプから誤って属性を取得してしまうという問題が発生します。属性はプロトタイプに残しておいて、新しいインスタンスにコピーしないでください。たとえば、clone
メソッドをに追加する場合Object.prototype
、一部の回答が示すように、その属性を明示的にスキップする必要があります。しかし、あなたが知らない に追加された他の追加メソッドObject.prototype
や他の中間プロトタイプがある場合はどうなるでしょうか? その場合、コピーしてはいけない属性をコピーすることになるため、予期しない非ローカル属性をhasOwnProperty
メソッドで検出する必要があります。
列挙不可能な属性に加えて、非表示のプロパティを持つオブジェクトをコピーしようとすると、より困難な問題が発生します。たとえばprototype
、関数の隠しプロパティです。また、オブジェクトのプロトタイプは属性で参照されます__proto__
が、これも非表示であり、ソース オブジェクトの属性を反復する for/in ループによってコピーされることはありません。__proto__
これは Firefox の JavaScript インタープリターに固有のものであり、他のブラウザーでは何かが異なる可能性があると思いますが、全体像はわかります。すべてが数えられるわけではありません。名前がわかっている場合は非表示の属性をコピーできますが、それを自動的に検出する方法はわかりません。
洗練された解決策を模索する上でのもう 1 つの問題は、プロトタイプの継承を正しく設定するという問題です。ソース オブジェクトのプロトタイプがObject
である場合は、 で新しい一般オブジェクトを作成するだけで{}
機能しますが、ソースのプロトタイプが の子孫である場合、フィルタObject
を使用してスキップした、またはhasOwnProperty
プロトタイプにはありましたが、そもそも列挙できませんでした。1 つの解決策は、ソース オブジェクトのプロパティを呼び出してconstructor
最初のコピー オブジェクトを取得し、属性をコピーすることですが、それでも列挙不可能な属性は取得できません。たとえば、Date
オブジェクトはそのデータを隠しメンバーとして格納します。
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
var d1 = new Date();
/* Executes function after 5 seconds. */
setTimeout(function(){
var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);
の日付文字列は、 の日付文字列d1
より 5 秒遅れd2
ます。Date
同じものを別のものと同じにする方法は、メソッドを呼び出すことですが、それはクラスsetTime
に固有です。Date
この問題に対する防弾の一般的な解決策はないと思いますが、間違っていれば幸いです!
一般的なディープ コピーを実装しなければならなかったとき、単純なObject
, Array
, Date
, String
,Number
またはBoolean
. 最後の 3 つのタイプは不変であるため、浅いコピーを実行でき、変更を心配する必要はありません。Object
さらに、 orに含まれる要素はすべてArray
、そのリストにある 6 つの単純な型の 1 つであると想定しました。これは、次のようなコードで実現できます。
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
上記の関数は、オブジェクトと配列のデータがツリー構造を形成している限り、前述の 6 つの単純な型に対して適切に機能します。つまり、オブジェクト内の同じデータへの参照は 1 つしかありません。例えば:
// This would be cloneable:
var tree = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"right" : null,
"data" : 8
};
// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;
JavaScriptオブジェクトを処理することはできませんが、投げたものすべてに対して機能すると想定しない限り、多くの目的には十分かもしれません.
Date
オブジェクト内で s、functions、undefined、regExp、または Infinity を使用しない場合、非常に単純なワンライナーはJSON.parse(JSON.stringify(object))
次のとおりです。
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()
これは、オブジェクト、配列、文字列、ブール値、および数値を含むすべての種類のオブジェクトで機能します。
ワーカーとの間でメッセージを投稿するときに使用されるブラウザーの構造化クローン アルゴリズムに関するこの記事も参照してください。また、ディープ クローニングの機能も含まれています。
jQuery を使用すると、 extendを使用して浅いコピーを行うことができます。
var copiedObject = jQuery.extend({}, originalObject)
へのその後の変更はcopiedObject
には影響しませんoriginalObject
。その逆も同様です。
またはディープコピーを作成するには:
var copiedObject = jQuery.extend(true, {}, originalObject)
ECMAScript 6 にはObject.assignメソッドがあり、列挙可能なすべての独自のプロパティの値をあるオブジェクトから別のオブジェクトにコピーします。例えば:
var x = {myProp: "value"};
var y = Object.assign({}, x);
ただし、これは浅いコピーであることに注意してください。ネストされたオブジェクトは参照としてコピーされます。
MDNあたり:
Object.assign({}, a)
JSON.parse(JSON.stringify(a))
外部ライブラリは必要ありませんが、最初にブラウザの互換性を確認する必要があります。
多くの答えがありますが、 ECMAScript 5 のObject.createについて言及しているものはありません。これは正確なコピーを提供しないことは確かですが、ソースを新しいオブジェクトのプロトタイプとして設定します。
したがって、これは質問に対する正確な答えではありませんが、1行のソリューションであり、エレガントです。また、次の 2 つの場合に最適です。
例:
var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property
このソリューションが優れていると考えるのはなぜですか? ネイティブであるため、ループも再帰もありません。ただし、古いブラウザにはポリフィルが必要です。
Object.assign
メソッドは ECMAScript 2015 (ES6) 標準の一部であり、必要なことを正確に実行します。
var clone = Object.assign({}, obj);
Object.assign() メソッドは、列挙可能なすべての独自のプロパティの値を 1 つ以上のソース オブジェクトからターゲット オブジェクトにコピーするために使用されます。
古いブラウザーをサポートするポリフィル:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
インターネット上のほとんどのソリューションにはいくつかの問題があります。そこで、受け入れられた回答が受け入れられない理由を含め、フォローアップを行うことにしました。
Javascriptをそのすべての子とその子などとともにディープ コピーしたいと考えています。Object
しかし、私は普通の開発者ではないのでObject
、通常 properties
のcircular structures
とnested objects
.
circular structure
それでは、最初に aと aを作成しましょう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
.
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"
}
}
...そしてご覧のとおり、内部のネストされた構造では機能しませんでした。
古いブラウザには IE 8 と同じようにポリフィルがありObject.create
ます。これは 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 でディープ コピーを実行する場合、プロパティが "this" であるため、サイクルを回避するにはどうすればよいですか? ) が見つかりましたが、より良い解決策があります。
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
しますが、クローン作成中にそれらのインスタンスを台無しにします。
浅いコピーでよければ、underscore.js ライブラリにcloneメソッドがあります。
y = _.clone(x);
または、次のように拡張できます
copiedObject = _.extend({},originalObject);
特にエレガントでない解決策の1つは、JSONエンコーディングを使用して、メンバーメソッドを持たないオブジェクトのディープコピーを作成することです。方法論は、ターゲットオブジェクトをJSONエンコードし、それをデコードすることで、探しているコピーを取得することです。必要な数のコピーを作成するために、何度でもデコードできます。
もちろん、関数はJSONに属していないため、これはメンバーメソッドのないオブジェクトに対してのみ機能します。
この方法論は私のユースケースに最適でした。JSONBLOBをKey-Valueストアに格納しており、JavaScript APIでオブジェクトとして公開されると、各オブジェクトには実際にオブジェクトの元の状態のコピーが含まれるため、呼び出し元が公開されたオブジェクトを変更した後、デルタを計算できます。
var object1 = {key:"value"};
var object2 = object1;
object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);
object2.key = "a change";
console.log(object1);// returns value
この記事から: Brian HuismanによるJavascript で配列とオブジェクトをコピーする方法:
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (var i in this) {
if (i == 'clone') continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
} else newObj[i] = this[i]
} return newObj;
};
AngularJS を使用している場合は、このライブラリ内のオブジェクトを複製または拡張するための直接的な方法もあります。
var destination = angular.copy(source);
また
angular.copy(source, destination);
angular.copyドキュメントの詳細...
function clone(obj) {
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = new obj.constructor();
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
A.Levyの答えはほぼ完成しています。ここに私の小さな貢献があります:再帰参照を処理する方法があります。この行を参照してください
if(this[attr]==this) copy[attr] = copy;
オブジェクトが XML DOM 要素の場合は、代わりにcloneNodeを使用する必要があります
if(this.cloneNode) return this.cloneNode(true);
A.Levy の徹底的な研究と Calvin のプロトタイピング アプローチに触発されて、私は次のソリューションを提供します。
Object.prototype.clone = function() {
if(this.cloneNode) return this.cloneNode(true);
var copy = this instanceof Array ? [] : {};
for(var attr in this) {
if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
copy[attr] = this[attr];
else if(this[attr]==this) copy[attr] = copy;
else copy[attr] = this[attr].clone();
}
return copy;
}
Date.prototype.clone = function() {
var copy = new Date();
copy.setTime(this.getTime());
return copy;
}
Number.prototype.clone =
Boolean.prototype.clone =
String.prototype.clone = function() {
return this;
}
回答の Andy Burke のメモも参照してください。
Lodash の使用:
var y = _.clone(x, true);
1 行のコードを使用して、オブジェクトを複製し、以前のオブジェクトから参照を削除できます。単に行う:
var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references
obj2.text = 'moo2'; // Only updates obj2's text property
console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}
現在 Object.create をサポートしていないブラウザー/エンジンでは、次のポリフィルを使用できます。
// Polyfill Object.create if it does not exist
if (!Object.create) {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
古い質問に対する新しい答え!Spread SyntaxでECMAScript 2016 (ES6) を使用する喜びがあれば、それは簡単です。
keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}
これにより、オブジェクトの浅いコピーのクリーンな方法が提供されます。再帰的にネストされたすべてのオブジェクトのすべての値の新しいコピーを作成することを意味する深いコピーを作成するには、上記のより重いソリューションが必要です。
JavaScript は進化し続けています。
ディープ コピーとクローンの場合、オブジェクトを JSON.stringify してから JSON.parse します。
obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
(以下は主に @ Maciej Bukowski、@ A. Levy、@ Jan Turoň、@ Reduの回答、および @ LeviRoberts、@ RobGのコメントの統合であり、彼らに感謝します!!!)
ディープコピー?- はい!(多くの場合);
浅いコピー? - いいえ!(除くProxy
)。
皆さんのテストを心から歓迎しますclone()
。
さらに、任意のタイプの記述子defineProp()
を簡単かつ迅速に(再) 定義またはコピーできるように設計されています。
function clone(object) {
/*
Deep copy objects by value rather than by reference,
exception: `Proxy`
*/
const seen = new WeakMap()
return clone(object)
function clone(object) {
if (object !== Object(object)) return object /*
—— Check if the object belongs to a primitive data type */
if (object instanceof Node) return object.cloneNode(true) /*
—— Clone DOM trees */
let _object // The clone of object
switch (object.constructor) {
case Array:
case Object:
_object = cloneObject(object)
break
case Date:
_object = new Date(+object)
break
case Function:
_object = copyFn(object)
break
case RegExp:
_object = new RegExp(object)
break
default:
switch (Object.prototype.toString.call(object.constructor)) {
// // Stem from:
case "[object Function]":
switch (object[Symbol.toStringTag]) {
case undefined:
_object = cloneObject(object) // `class`
break
case "AsyncFunction":
case "GeneratorFunction":
case "AsyncGeneratorFunction":
_object = copyFn(object)
break
default:
_object = object
}
break
case "[object Undefined]": // `Object.create(null)`
_object = cloneObject(object)
break
default:
_object = object // `Proxy`
}
}
return _object
}
function cloneObject(object) {
if (seen.has(object)) return seen.get(object) /*
—— Handle recursive references (circular structures) */
const _object = Array.isArray(object)
? []
: Object.create(Object.getPrototypeOf(object)) /*
—— Assign [[Prototype]] for inheritance */
seen.set(object, _object) /*
—— Make `_object` the associative mirror of `object` */
Reflect.ownKeys(object).forEach(key =>
defineProp(_object, key, { value: clone(object[key]) }, object)
)
return _object
}
}
function copyPropDescs(target, source) {
Object.defineProperties(target,
Object.getOwnPropertyDescriptors(source)
)
}
function convertFnToStr(fn) {
let fnStr = String(fn)
if (fn.name.startsWith("[")) // isSymbolKey
fnStr = fnStr.replace(/\[Symbol\..+?\]/, '')
fnStr = /^(?!(async )?(function\b|[^{]+?=>))[^(]+?\(/.test(fnStr)
? fnStr.replace(/^(async )?(\*)?/, "$1function$2 ") : fnStr
return fnStr
}
function copyFn(fn) {
const newFn = new Function(`return ${convertFnToStr(fn)}`)()
copyPropDescs(newFn, fn)
return newFn
}
function defineProp(object, key, descriptor = {}, copyFrom = {}) {
const { configurable: _configurable, writable: _writable }
= Object.getOwnPropertyDescriptor(object, key)
|| { configurable: true, writable: true }
const test = _configurable // Can redefine property
&& (_writable === undefined || _writable) // Can assign to property
if (!test || arguments.length <= 2) return test
const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
|| { configurable: true, writable: true } // Custom…
|| {}; // …or left to native default settings
["get", "set", "value", "writable", "enumerable", "configurable"]
.forEach(attr =>
descriptor[attr] === undefined &&
(descriptor[attr] = basisDesc[attr])
)
const { get, set, value, writable, enumerable, configurable }
= descriptor
return Object.defineProperty(object, key, {
enumerable, configurable, ...get || set
? { get, set } // Accessor descriptor
: { value, writable } // Data descriptor
})
}
const obj0 = {
u: undefined,
nul: null,
t: true,
num: 9,
str: "",
sym: Symbol("symbol"),
[Symbol("e")]: Math.E,
arr: [[0], [1, 2]],
d: new Date(),
re: /f/g,
get g() { return 0 },
o: {
n: 0,
o: { f: function (...args) { } }
},
f: {
getAccessorStr(object) {
return []
.concat(...
Object.values(Object.getOwnPropertyDescriptors(object))
.filter(desc => desc.writable === undefined)
.map(desc => Object.values(desc))
)
.filter(prop => typeof prop === "function")
.map(String)
},
f0: function f0() { },
f1: function () { },
f2: a => a / (a + 1),
f3: () => 0,
f4(params) { return param => param + params },
f5: (a, b) => ({ c = 0 } = {}) => a + b + c
}
}
defineProp(obj0, "s", { set(v) { this._s = v } })
defineProp(obj0.arr, "tint", { value: { is: "non-enumerable" } })
obj0.arr[0].name = "nested array"
let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", { set(v) { this._s = v + 1 } })
defineProp(obj1.re, "multiline", { value: true })
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Routinely")
console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()
console.log("obj0\n ",
".arr.tint:", obj0.arr.tint, "\n ",
".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
".arr.tint:", obj1.arr.tint, "\n ",
".arr[0].name:", obj1.arr[0].name
)
console.log()
console.log("Accessor-type descriptor\n ",
"of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
"of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
"set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
" → (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)
console.log("—— obj0 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - More kinds of functions")
const fnsForTest = {
f(_) { return _ },
func: _ => _,
aFunc: async _ => _,
async function() { },
async asyncFunc() { },
aFn: async function () { },
*gen() { },
async *asyncGen() { },
aG1: async function* () { },
aG2: async function* gen() { },
*[Symbol.iterator]() { yield* Object.keys(this) }
}
console.log(Reflect.ownKeys(fnsForTest).map(k =>
`${String(k)}:
${fnsForTest[k].name}-->
${String(fnsForTest[k])}`
).join("\n"))
const normedFnsStr = `{
f: function f(_) { return _ },
func: _ => _,
aFunc: async _ => _,
function: async function() { },
asyncFunc: async function asyncFunc() { },
aFn: async function () { },
gen: function* gen() { },
asyncGen: async function* asyncGen() { },
aG1: async function* () { },
aG2: async function* gen() { },
[Symbol.iterator]: function* () { yield* Object.keys(this) }
}`
const copiedFnsForTest = clone(fnsForTest)
console.log("fnsForTest:", fnsForTest)
console.log("fnsForTest (copied):", copiedFnsForTest)
console.log("fnsForTest (normed str):", eval(`(${normedFnsStr})`))
console.log("Comparison of fnsForTest and its clone:",
Reflect.ownKeys(fnsForTest).map(k =>
[k, fnsForTest[k] === copiedFnsForTest[k]]
)
)
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Circular structures")
obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr
obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log("Clear obj0's recursion:",
obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
"obj0\n ",
".o.r:", obj0.o.r, "\n ",
".arr:", obj0.arr
)
console.log(
"obj1\n ",
".o.r:", obj1.o.r, "\n ",
".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Classes")
class Person {
constructor(name) {
this.name = name
}
}
class Boy extends Person { }
Boy.prototype.sex = "M"
const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }
const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"
boy0.name = "one"
boy1.name = "neo"
console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)
Object.create()
| | MDNObject.defineProperties()
| | MDNこれは、関数の複製と複数/循環参照も処理するためのA. Levyのコードの適応です。つまり、複製されるツリー内の2つのプロパティが同じオブジェクトの参照である場合、複製されるオブジェクトツリーにはこれらが含まれます。プロパティは、参照されるオブジェクトの1つの同じクローンを指します。これにより、循環依存の場合も解決されます。この依存関係は、未処理のままにすると、無限ループになります。アルゴリズムの複雑さはO(n)です。
function clone(obj){
var clonedObjectsArray = [];
var originalObjectsArray = []; //used to remove the unique ids when finished
var next_objid = 0;
function objectId(obj) {
if (obj == null) return null;
if (obj.__obj_id == undefined){
obj.__obj_id = next_objid++;
originalObjectsArray[obj.__obj_id] = obj;
}
return obj.__obj_id;
}
function cloneRecursive(obj) {
if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") 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; i < obj.length; ++i) {
copy[i] = cloneRecursive(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
if (clonedObjectsArray[objectId(obj)] != undefined)
return clonedObjectsArray[objectId(obj)];
var copy;
if (obj instanceof Function)//Handle Function
copy = function(){return obj.apply(this, arguments);};
else
copy = {};
clonedObjectsArray[objectId(obj)] = copy;
for (var attr in obj)
if (attr != "__obj_id" && obj.hasOwnProperty(attr))
copy[attr] = cloneRecursive(obj[attr]);
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
var cloneObj = cloneRecursive(obj);
//remove the unique ids
for (var i = 0; i < originalObjectsArray.length; i++)
{
delete originalObjectsArray[i].__obj_id;
};
return cloneObj;
}
いくつかの簡単なテスト
var auxobj = {
prop1 : "prop1 aux val",
prop2 : ["prop2 item1", "prop2 item2"]
};
var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;
obj.f1 = function (){
this.prop1 = "prop1 val changed by f1";
};
objclone = clone(obj);
//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));
objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));
objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
mindeavorは、複製されるオブジェクトは「リテラルで構築された」オブジェクトであると述べているため、解決策は、オブジェクトのインスタンスを複製するのではなく、単にオブジェクトを複数回生成することです。
function createMyObject()
{
var myObject =
{
...
};
return myObject;
}
var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
この投稿のすべてのソリューションに追加したかったのObject.create
ですが、これはnodejsでは希望どおりに機能しません。
Firefoxでは、
var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´
は
{test:"test"}
。
nodejsでは
{}
私は自分の実装を書きました。それがより良い解決策と見なされるかどうかはわかりません:
/*
a function for deep cloning objects that contains other nested objects and circular structures.
objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
index (z)
|
|
|
|
|
| depth (x)
|_ _ _ _ _ _ _ _ _ _ _ _
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/...../
/................./
/..... /
/ /
/------------------
object length (y) /
*/
実装は次のとおりです。
function deepClone(obj) {
var depth = -1;
var arr = [];
return clone(obj, arr, depth);
}
/**
*
* @param obj source object
* @param arr 3D array to store the references to objects
* @param depth depth of the current object relative to the passed 'obj'
* @returns {*}
*/
function clone(obj, arr, depth){
if (typeof obj !== "object") {
return obj;
}
var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'
var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
if(result instanceof Array){
result.length = length;
}
depth++; // depth is increased because we entered an object here
arr[depth] = []; // this is the x-axis, each index here is the depth
arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
// start the depth at current and go down, cyclic structures won't form on depths more than the current one
for(var x = depth; x >= 0; x--){
// loop only if the array at this depth and length already have elements
if(arr[x][length]){
for(var index = 0; index < arr[x][length].length; index++){
if(obj === arr[x][length][index]){
return obj;
}
}
}
}
arr[depth][length].push(obj); // store the object in the array at the current depth and length
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
}
return result;
}
function clone(src, deep) {
var toString = Object.prototype.toString;
if(!src && typeof src != "object"){
//any non-object ( Boolean, String, Number ), null, undefined, NaN
return src;
}
//Honor native/custom clone methods
if(src.clone && toString.call(src.clone) == "[object Function]"){
return src.clone(deep);
}
//DOM Elements
if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
return src.cloneNode(deep);
}
//Date
if(toString.call(src) == "[object Date]"){
return new Date(src.getTime());
}
//RegExp
if(toString.call(src) == "[object RegExp]"){
return new RegExp(src);
}
//Function
if(toString.call(src) == "[object Function]"){
//Wrap in another method to make sure == is not true;
//Note: Huge performance issue due to closures, comment this :)
return (function(){
src.apply(this, arguments);
});
}
var ret, index;
//Array
if(toString.call(src) == "[object Array]"){
//[].slice(0) would soft clone
ret = src.slice();
if(deep){
index = ret.length;
while(index--){
ret[index] = clone(ret[index], true);
}
}
}
//Object
else {
ret = src.constructor ? new src.constructor() : {};
for (var prop in src) {
ret[prop] = deep
? clone(src[prop], true)
: src[prop];
}
}
return ret;
};
から使用deepcopy
しnpm
ます。ブラウザnode
とnpm module...
https://www.npmjs.com/package/deepcopy
let a = deepcopy(b)
オブジェクトを複製する単純な再帰的方法。lodash.clone も使用できます。
let clone = (obj) => {
let obj2 = Array.isArray(obj) ? [] : {};
for(let k in obj) {
obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) : obj[k];
}
return obj2;
}
let w = { name: "Apple", types: ["Fuji", "Gala"]};
let x = clone(w);
w.name = "Orange";
w.types = ["Navel"];
console.log(x);
console.log(w);
に基づいてオブジェクトを複製しますtemplate
。正確なコピーは必要ないが、ある種の信頼できるクローン操作の堅牢性が必要であるが、ビットのクローンのみが必要な場合、または各属性値の存在または形式を確実に制御できるようにしたい場合はどうしますか?クローン?
これは私たちにとって有用であり、同様のものが見つからなかったため作成したため、これを提供しています。これを使用して、複製したいオブジェクトの属性を指定するオブジェクトに基づいてオブジェクトを複製できtemplate
ます。また、テンプレートを使用すると、ソースオブジェクトに存在しない場合、またはソースオブジェクトに存在しない場合、関数がそれらの属性を別のものに変換できます。クローンを処理したい。役に立たない場合は、誰かがこの回答を削除できると確信しています。
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
function cloneObjectByTemplate(obj, tpl, cloneConstructor) {
if (typeof cloneConstructor === "undefined") {
cloneConstructor = false;
}
if (obj == null || typeof (obj) != 'object') return obj;
//if we have an array, work through it's contents and apply the template to each item...
if (Array.isArray(obj)) {
var ret = [];
for (var i = 0; i < obj.length; i++) {
ret.push(cloneObjectByTemplate(obj[i], tpl, cloneConstructor));
}
return ret;
}
//otherwise we have an object...
//var temp:any = {}; // obj.constructor(); // we can't call obj.constructor because typescript defines this, so if we are dealing with a typescript object it might reset values.
var temp = cloneConstructor ? new obj.constructor() : {};
for (var key in tpl) {
//if we are provided with a function to determine the value of this property, call it...
if (isFunction(tpl[key])) {
temp[key] = tpl[key](obj); //assign the result of the function call, passing in the value
} else {
//if our object has this property...
if (obj[key] != undefined) {
if (Array.isArray(obj[key])) {
temp[key] = [];
for (var i = 0; i < obj[key].length; i++) {
temp[key].push(cloneObjectByTemplate(obj[key][i], tpl[key], cloneConstructor));
}
} else {
temp[key] = cloneObjectByTemplate(obj[key], tpl[key], cloneConstructor);
}
}
}
}
return temp;
}
それを呼び出す簡単な方法は次のようになります。
var source = {
a: "whatever",
b: {
x: "yeah",
y: "haha"
}
};
var template = {
a: true, //we want to clone "a"
b: {
x: true //we want to clone "b.x" too
}
};
var destination = cloneObjectByTemplate(source, template);
関数を使用して、属性が返されることを確認したり、特定の型であることを確認したりする場合は、次のようなテンプレートを使用します。を使用する代わりに、ソース オブジェクトの を{ ID: true }
コピーするだけの関数を提供していID attribute
ますが、ソース オブジェクトに存在しない場合でも、それが数値であることを確認します。
var template = {
ID: function (srcObj) {
if(srcObj.ID == undefined){ return -1; }
return parseInt(srcObj.ID.toString());
}
}
配列は問題なく複製されますが、必要に応じて、独自の関数でこれらの個々の属性も処理し、次のような特別なことを行うことができます。
var template = {
tags: function (srcObj) {
var tags = [];
if (process.tags != undefined) {
for (var i = 0; i < process.tags.length; i++) {
tags.push(cloneObjectByTemplate(
srcObj.tags[i],
{ a : true, b : true } //another template for each item in the array
);
}
}
return tags;
}
}
したがって、上記では、テンプレートtags
はソース オブジェクトの属性が存在する場合はそれをコピーするだけであり (配列であると想定されます)、その配列内の各要素に対して clone 関数が呼び出され、2 番目のテンプレートに基づいて個別に複製されます。これらの各タグ要素の属性a
と属性をコピーするだけです。b
ノードの内外でオブジェクトを取得していて、それらのオブジェクトのどのアトリビュートを複製するかを制御したい場合、これはそれを制御する優れた方法でnode.js
あり、コードはブラウザでも機能します。
使用例を次に示します: http://jsfiddle.net/hjchyLt1/
ファンクショナル クロージャーを使用すると、ディープ コピーを使用せずに、ディープ コピーのすべての利点を得ることができます。これは非常に異なるパラダイムですが、うまく機能します。既存のオブジェクトをコピーしようとする代わりに、必要なときに関数を使用して新しいオブジェクトをインスタンス化します。
まず、オブジェクトを返す関数を作成します
function template() {
return {
values: [1, 2, 3],
nest: {x: {a: "a", b: "b"}, y: 100}
};
}
次に、単純な浅いコピー関数を作成します
function copy(a, b) {
Object.keys(b).forEach(function(key) {
a[key] = b[key];
});
}
新しいオブジェクトを作成し、テンプレートのプロパティをコピーします
var newObject = {};
copy(newObject, template());
ただし、上記のコピー手順は必要ありません。あなたがする必要があるのはこれだけです:
var newObject = template();
新しいオブジェクトが作成されたので、そのプロパティをテストして確認します。
console.log(Object.keys(newObject));
これは次のように表示されます。
["values", "nest"]
はい、それらは newObject 自身のプロパティであり、別のオブジェクトのプロパティへの参照ではありません。確認してみましょう:
console.log(newObject.nest.x.b);
これは次のように表示されます。
"b"
newObject はテンプレート オブジェクトのすべてのプロパティを取得していますが、依存関係の連鎖はありません。
http://jsbin.com/ISUTIpoC/1/edit?js、コンソール
議論を促すためにこの例を追加したので、コメントを追加してください:)
私のコードでは、function (_)
コピーを処理する を頻繁に定義してby value
、関数に渡すことができるようにしています。このコードはディープ コピーを作成しますが、継承は維持します。また、自己参照オブジェクトを無限ループなしでコピーできるように、サブコピーも追跡します。お気軽にご利用ください。
最も洗練されたものではないかもしれませんが、まだ失敗したことはありません。
_ = function(oReferance) {
var aReferances = new Array();
var getPrototypeOf = function(oObject) {
if(typeof(Object.getPrototypeOf)!=="undefined") return Object.getPrototypeOf(oObject);
var oTest = new Object();
if(typeof(oObject.__proto__)!=="undefined"&&typeof(oTest.__proto__)!=="undefined"&&oTest.__proto__===Object.prototype) return oObject.__proto__;
if(typeof(oObject.constructor)!=="undefined"&&typeof(oTest.constructor)!=="undefined"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!=="undefined") return oObject.constructor.prototype;
return Object.prototype;
};
var recursiveCopy = function(oSource) {
if(typeof(oSource)!=="object") return oSource;
if(oSource===null) return null;
for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];
var Copy = new Function();
Copy.prototype = getPrototypeOf(oSource);
var oCopy = new Copy();
aReferances.push([oSource,oCopy]);
for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);
return oCopy;
};
return recursiveCopy(oReferance);
};
// Examples:
Wigit = function(){};
Wigit.prototype.bInThePrototype = true;
A = new Wigit();
A.nCoolNumber = 7;
B = _(A);
B.nCoolNumber = 8; // A.nCoolNumber is still 7
B.bInThePrototype // true
B instanceof Wigit // true
オブジェクトに循環依存関係がない場合は、他の回答のいずれかまたはjQuery のコピー メソッドを使用することをお勧めします。これらはすべて非常に効果的です。
循環的な依存関係 (つまり、2 つのサブオブジェクトが相互にリンクしている) がある場合、(理論的な観点から)この問題をエレガントに解決する方法がないため、ちょっと困ったことになります。
これにより、 (参照だけでなく)新しいコピーobj
が作成されます。
let myCopy = JSON.parse(JSON.stringify(obj));
..その後、はるかに効率的に動作し_.cloneDeep(obj)
ます。
Apple JavaScriptコーディングガイドラインから:
// Create an inner object with a variable x whose default
// value is 3.
function innerObj()
{
this.x = 3;
}
innerObj.prototype.clone = function() {
var temp = new innerObj();
for (myvar in this) {
// this object does not contain any objects, so
// use the lightweight copy code.
temp[myvar] = this[myvar];
}
return temp;
}
// Create an outer object with a variable y whose default
// value is 77.
function outerObj()
{
// The outer object contains an inner object. Allocate it here.
this.inner = new innerObj();
this.y = 77;
}
outerObj.prototype.clone = function() {
var temp = new outerObj();
for (myvar in this) {
if (this[myvar].clone) {
// This variable contains an object with a
// clone operator. Call it to create a copy.
temp[myvar] = this[myvar].clone();
} else {
// This variable contains a scalar value,
// a string value, or an object with no
// clone function. Assign it directly.
temp[myvar] = this[myvar];
}
}
return temp;
}
// Allocate an outer object and assign non-default values to variables in
// both the outer and inner objects.
outer = new outerObj;
outer.inner.x = 4;
outer.y = 16;
// Clone the outer object (which, in turn, clones the inner object).
newouter = outer.clone();
// Verify that both values were copied.
alert('inner x is '+newouter.inner.x); // prints 4
alert('y is '+newouter.y); // prints 16
スティーブ
//
// creates 'clone' method on context object
//
// var
// clon = Object.clone( anyValue );
//
!((function (propertyName, definition) {
this[propertyName] = definition();
}).call(
Object,
"clone",
function () {
function isfn(fn) {
return typeof fn === "function";
}
function isobj(o) {
return o === Object(o);
}
function isarray(o) {
return Object.prototype.toString.call(o) === "[object Array]";
}
function fnclon(fn) {
return function () {
fn.apply(this, arguments);
};
}
function owns(obj, p) {
return obj.hasOwnProperty(p);
}
function isemptyobj(obj) {
for (var p in obj) {
return false;
}
return true;
}
function isObject(o) {
return Object.prototype.toString.call(o) === "[object Object]";
}
return function (input) {
if (isfn(input)) {
return fnclon(input);
} else if (isobj(input)) {
var cloned = {};
for (var p in input) {
owns(Object.prototype, p)
|| (
isfn(input[p])
&& ( cloned[p] = function () { return input[p].apply(input, arguments); } )
|| ( cloned[p] = input[p] )
);
}
if (isarray(input)) {
cloned.length = input.length;
"concat every filter forEach indexOf join lastIndexOf map pop push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift"
.split(" ")
.forEach(
function (methodName) {
isfn( Array.prototype[methodName] )
&& (
cloned[methodName] =
function () {
return Array.prototype[methodName].apply(cloned, arguments);
}
);
}
);
}
return isemptyobj(cloned)
? (
isObject(input)
? cloned
: input
)
: cloned;
} else {
return input;
}
};
}
));
//
JSON.stringify
処理できない循環オブジェクトを処理するには、任意のグラフを JSON 形式にシリアル化および逆シリアル化するJSOGというライブラリを使用できます。
var clone = JSOG.parse(JSOG.stringify(original));
このトリックを使用して、クローン作成のために JSOG にパッチを適用することも興味深いかもしれません (現時点では時間がありませんが、誰かが試してみたい場合は...):
シンプルな関数をシリアライズします:
foo.f = function(a) { return a }
var stringForm = foo.f.toString() // "function (a) { return a }"
関数を逆シリアル化します:
eval("foo.f = " + stringForm)
関数と通常の文字列を識別するためのいくつかの規則 (おそらくプロパティの名前) が必要になります (@func_f
おそらく)。
もちろん、関数が 2 番目の関数を呼び出す場合、2 番目の関数は元の関数と同じように存在する必要があります。
ただし、信頼できないソースからシリアル化された形式を受け入れる場合、上記は非常に危険ですが、信頼できないソースから任意の形式の関数を受け入れることは危険です。そのため、関数のクローン作成に関心がある場合は、信頼がすでに確立されている必要があります。 (または、すでにセキュリティ上の欠陥を書くつもりです!)。
免責事項: JSOG stringify/parse と JSON stringify/parse の速度はテストしていませんが、テストした単純な (循環) オブジェクトでは動作します。
関数を持つオブジェクトを取得した場合は、JSONfn で実行できます。 http://www.eslinstructor.net/jsonfn/を参照してください。
var obj= {
name:'Marvin',
getName : function(){
return this.name;
}
}
var cobj = JSONfn.parse(JSONfn.stringify(obj));
最終的にそれ自体を指す可能性のあるオブジェクトをコピーする際の問題は、簡単なチェックで解決できます。コピー アクションがあるたびに、このチェックを追加します。遅いかもしれませんが、動作するはずです。
toType()関数を使用して、明示的にオブジェクト タイプを返します。また、独自のcopyObj()関数もありますが、これはロジックがかなり似ており、Object()、Array()、および Date() の 3 つのケースすべてに応答します。
NodeJSで実行します。
まだテストされていません。
// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
if (toType(parent) == '[object Object]') {
for (var name in parent) {
var curProperty = parent[name];
// Direct child.
if (curProperty = target) return true;
// Check if target is a child of this property, and so on, recursively.
if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
if (isChild(target, curProperty)) return true;
}
}
} else if (toType(parent) == '[object Array]') {
for (var i=0; i < parent.length; i++) {
var curItem = parent[i];
// Direct child.
if (curItem = target) return true;
// Check if target is a child of this property, and so on, recursively.
if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
if (isChild(target, curItem)) return true;
}
}
}
return false; // Not the target.
}