JavaScriptでディープクローンする方法


105

JavaScriptオブジェクトをどのようにディープクローンしますか?

私はのようなフレームワークに基づいたさまざまな機能があります知っているJSON.parse(JSON.stringify(o))$.extend(true, {}, o)私はそれのようなフレームワークを使用する必要はありません。

ディープクローンを作成する最もエレガントで効率的な方法は何ですか。

アレイのクローンのようなエッジケースも気にします。プロトタイプチェーンを壊さず、自己参照を扱います。

DOMオブジェクトなどのコピーをサポートする必要はありません.cloneNode。その理由は、そのため存在しているからです。

私は主にディープクローンを使用したいのでnode.js、V8エンジンのES5機能を使用できます。

[編集]

だれかが提案する前に、オブジェクトからプロトタイプ的に継承してコピーを作成することとそれを複製することの間には明確な違いがあります。前者はプロトタイプチェーンを混乱させます。

[さらに編集]

あなたの答えを読んだ後、オブジェクト全体を複製することは非常に危険で難しいゲームであるという迷惑な発見に行きました。たとえば、次のクロージャベースのオブジェクトを考えてみましょう

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

オブジェクトを複製する複製関数を作成する方法はありますか?複製時の状態は同じですがo、JSでJSパーサーを作成しないと状態を変更できません。

このような関数は、もはや現実には必要ありません。これは単なる学術的な興味です。


2
重複としてマークされる前に、stackoverflow.com / questions / 122102 / …を調べたところ、すべてのエッジケースに対処する回答が見つかりませんでした。
レイノス

そのようなプライベート変数は真にプライベートであり、その結果ジェネリックclone関数からアクセスできないため、「さらなる編集」セクションの要件は、オブジェクト自体からの「ヘルプ」なしでは達成できません。問題のオブジェクトは、独自のオーダーメードcloneメソッドを公​​開する必要があります。
トリンコット

私は今夜​​これについてたくさん読んでいて、見つけたリソースの中に、ブラウザで構造化クローンアルゴリズムにアクセスするためのいくつかのハックが含まれているこの醜いブログの投稿があります:dassur.ma/things/deep-copy

回答:


67

それはあなたがクローンしたいものに本当に依存します。これは本当にJSONオブジェクトですか、それともJavaScriptの任意のオブジェクトですか?クローンを作成する場合、問題が発生する可能性があります。どのトラブル?以下で説明しますが、最初に、オブジェクトリテラル、プリミティブ、配列、DOMノードを複製するコード例を示します。

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

次に、REALオブジェクトの複製を開始するときに発生する可能性がある問題について説明します。私は今、あなたが次のようなことをして作成したオブジェクトについて話している

var User = function(){}
var newuser = new User();

もちろん、それらを複製できます。問題ありません。すべてのオブジェクトがコンストラクタープロパティを公開し、それを使用してオブジェクトを複製できますが、常に機能するとは限りません。for inこのオブジェクトに対して単純なこともできますが、同じ方向に進みます-トラブル。コードにはクローン機能も含まれていますが、if( false )ステートメントによって除外されています。

では、なぜクローニングが面倒なのでしょうか?さて、まず第一に、すべてのオブジェクト/インスタンスにはいくつかの状態があるかもしれません。たとえば、オブジェクトにプライベート変数がないことを確認することはできません。その場合は、オブジェクトのクローンを作成することで、状態を破壊するだけです。

状態がないと想像してください、それは結構です。その後、まだ別の問題があります。「コンストラクタ」メソッドによるクローン作成には、別の障害があります。これは引数の依存関係です。このオブジェクトを作成した人が作成しなかった、ある種のことを確認することはできません

new User({
   bike : someBikeInstance
});

これが事実である場合、あなたは運が悪いです、someBikeInstanceはおそらくいくつかのコンテキストで作成されており、そのコンテキストはクローンメソッドでは不明です。

じゃあ何をすればいいの?あなたはまだfor in解決を行うことができ、そのようなオブジェクトを通常のオブジェクトリテラルのように扱いますが、おそらくそのようなオブジェクトをまったく複製せずに、このオブジェクトの参照を渡すだけのアイデアですか?

別の解決策は、複製する必要があるすべてのオブジェクトがこの部分を自分で実装し、適切なAPIメソッド(cloneObjectなど)を提供するという規則を設定することです。cloneNodeDOMに対して何が行われているのか。

あなたが決める。


私は、クロージャーを使用して状態を非表示にするオブジェクトを処理するハードルを手に入れました。どのようにしてオブジェクトとその全体の状態を複製することができますか?result = new item.constructor();悪い点といえば、コンストラクター関数とアイテムオブジェクトが与えられれば、コンストラクターに渡されたパラメーターをREできるはずです。
レイノス

7
@Raynos:オブジェクトが状態を隠すためにクロージャを使用している場合、それらを複製することはできません。したがって、「閉鎖」という用語。最後にnemisjが言うように、最善の方法は、それがオプションである場合、クローン作成(またはシリアライゼーション/デシリアライゼーション)のAPIメソッドを実装することです。
Michiel Kalkman、2010

@MichielKalkman私はそうだった感じがしました。誰かがこれに対して本当に賢い解決策を持っているかもしれませんが。
レイノス

の効果はfalse && item.constructor何ですか?それはないですif役に立ちませんか?
Gabriel Petrovay 2014

2
@GabrielPetrovayこれifは決して実行されないため、機能の観点からは「役に立たない」が、使用しようとする可能性のある架空の実装を示すという学術的な目的があります。したがって、はい、else条件が評価されるたびに句がトリガーされますが、コードが存在する理由があります。
今村桂

154

非常に単純な方法、多分単純すぎる:

var cloned = JSON.parse(JSON.stringify(objectToClone));

12
オブジェクトの値が関数である場合を除き、すばらしい。その時点では、受け入れられた答えのようなものを使用する必要があるだろう。またはcloneDeep、Lodashのようにヘルパー関数を使用します。
matthoiland 2015

31
オブジェクト値が関数の場合、オブジェクトはJSONではありません。
Jos de Jong

5
関数を単に使用するのではなく、クローンするために正当化できるユースケースはどれですか
G. Ghez 2016年

3
私が正しく覚えているなら、これも日付を文字列に変換します。
Peter

3
G.Ghez @あなたは...機能が失われるという機能が含まれているオブジェクトのクローンを作成する場合
ピーター

38

JSON.parse(JSON.stringify())それはJSONデータのためのものだったとしてディープコピーJavascriptのオブジェクトへの組み合わせは、無効ハックです。undefinedまたはの値はサポートされていません。JavaScriptオブジェクトをJSONに「ストリング化」(マーシャリング)するときfunction () {}nullそれらの値は無視されます。

より良い解決策は、ディープコピー機能を使用することです。以下の関数はオブジェクトをディープコピーし、サードパーティライブラリ(jQuery、LoDashなど)を必要としません。

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

7
aObject(またはそれに含まれる別のオブジェクト)がそれ自体への自己参照を含む場合を除いて... stackoverflow™!
e2-e4 2017年

@ringø-「自己参照」テストケースをいくつか提供できますか?
tfmontague 2017年

4
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
e2-e4 2017年

4
私はこの解決策が好きです。私にとって唯一の修正は、NULL値を扱った: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
デヴィッド・カークランド

2
この機能はシンプルで理解しやすく、ほとんどすべてのケースに対応します。JavaScriptの世界では、それはあなたが得ることができるのとほぼ同じくらい完璧です。
icc97 2018

21

循環参照を持つオブジェクトでも機能するES6関数を次に示します。

function deepClone(obj, hash = new WeakMap()) {
    if (Object(obj) !== obj) return obj; // primitives
    if (hash.has(obj)) return hash.get(obj); // cyclic reference
    const result = obj instanceof Set ? new Set(obj) // See note about this!
                 : obj instanceof Map ? new Map(Array.from(obj, ([key, val]) => 
                                        [key, deepClone(val, hash)])) 
                 : obj instanceof Date ? new Date(obj)
                 : obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
                 // ... add here any specific treatment for other classes ...
                 // and finally a catch-all:
                 : obj.constructor ? new obj.constructor() 
                 : Object.create(null);
    hash.set(obj, result);
    return Object.assign(result, ...Object.keys(obj).map(
        key => ({ [key]: deepClone(obj[key], hash) }) ));
}

// Sample data
var p = {
  data: 1,
  children: [{
    data: 2,
    parent: null
  }]
};
p.children[0].parent = p;

var q = deepClone(p);

console.log(q.children[0].parent.data); // 1

セットとマップに関する注意

セットとマップのキーを処理する方法は議論の余地があります。これらのキーは多くの場合プリミティブです(この場合、議論はありません)が、オブジェクトにすることもできます。その場合、問題は次のようになります。これらのキーを複製する必要がありますか?

これが行われるべきであると主張することができるので、それらのオブジェクトがコピーで変更された場合、元のオブジェクトは影響を受けず、その逆も同様です。

一方has、キーを設定/マップする場合、少なくともどちらかが変更される前に、オリジナルとコピーの両方でこれが当てはまる必要があります。(複製プロセス中に作成されたため)コピーが以前に発生したことのないキーを持つSet / Mapである場合は奇妙です。確かに、指定されたオブジェクトがオブジェクトであるかどうかを知る必要があるコードにとって、これはあまり役に立ちません。そのSet / Mapにキーを入力するかどうか。

お気づきのように、私はセカンドオピニオンのほうが多いです。セットとマップのキーは、同じままであるはずの(多分参照)です。

このような選択肢は、他の(おそらくカスタムの)オブジェクトにも表示されます。クローンされたオブジェクトが特定のケースでどのように動作することが期待されるかに大きく依存するため、一般的な解決策はありません。


は日付と正規表現を処理しません
mkeremguc

1
@mkeremguc、コメントありがとうございます。日付と正規表現をサポートするようにコードを更新しました。
トリンコット2017年

マップを含む素晴らしいソリューション。es6セットのクローン作成のサポートがありません。
Robert Biggs 2018年

私は間違っていない場合は、おそらくとセットのサポートを追加することができます:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
ロバート・ビッグス

@RobertBiggs、これは1つの可能性ですが、私の意見では、セットに特定のキーがある場合、それはそのセットのクローンバージョンにも当てはまるはずです。提案されたコードでは、キーがオブジェクトの場合、これは当てはまりません。したがって、私はキーを複製しないことをお勧めします-実際には、それは期待どおりに動作するでしょう。その点で私の回答の更新を参照してください。
トリンコット

10

Underscore.jsのcontribライブラリライブラリは、呼び出された関数があるスナップショットを深いクローンオブジェクトこと

ソースからの抜粋:

snapshot: function(obj) {
  if(obj == null || typeof(obj) != 'object') {
    return obj;
  }

  var temp = new obj.constructor();

  for(var key in obj) {
    if (obj.hasOwnProperty(key)) {
      temp[key] = _.snapshot(obj[key]);
    }
  }

  return temp;
}

ライブラリがプロジェクトにリンクされたら、関数を呼び出すだけで

_.snapshot(object);

4
良い解決策、覚えておくべきポイント:クローンとオリジナルは同じプロトタイプを共有しています。それが問題である場合、「temp .__ proto__ = .snapshot(obj .__ proto_);」を追加することが可能です。「戻り温度」のすぐ上にあり、「列挙なし」とマークされたプロパティを持つビルドインクラスをサポートするには、「for(var key in obj)」の代わりにgetOwnPropertyNames()で反復できます
Ronen Ness

3

これは私が使っているディープクローニングの方法です。すばらしいと思います。

function deepClone (obj) {
    var _out = new obj.constructor;

    var getType = function (n) {
        return Object.prototype.toString.call(n).slice(8, -1);
    }

    for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
            _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
        }
    }
    return _out;
}

3

他の人がこの質問や同様の質問に言及しているように、「オブジェクト」のクローン作成は、一般的な意味で、JavaScriptには疑わしいものです。

しかし、私が「データ」オブジェクトと呼ぶオブジェクトのクラスがあります。つまり、{ ... }リテラルや単純なプロパティ割り当てから単純に構築されたオブジェクト、またはクローンを作成するのが妥当なJSONからデシリアライズされたオブジェクトです。ちょうど今日、私はサーバーから受け取ったデータを5倍に人工的に膨らませて、大きなデータセットで何が起こるかをテストしたかったのですが、オブジェクト(配列)とその子は、正しく機能するために別個のオブジェクトである必要がありました。クローニングにより、これを実行してデータセットを増やすことができました:

return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));

データオブジェクトのクローンを作成することが多いもう1つの場所は、送信する前にデータモデルのオブジェクトから状態フィールドを削除するホストにデータを送信することです。たとえば、「_」で始まるすべてのフィールドを、オブジェクトが複製されるときにオブジェクトから削除したい場合があります。

これは、サポートする配列と、複製するメンバーを選択するセレクター(「パス」文字列を使用してコンテキストを決定する)を含め、これを一般的に行うために作成したコードです。

function clone(obj,sel) {
    return (obj ? _clone("",obj,sel) : obj);
    }

function _clone(pth,src,sel) {
    var ret=(src instanceof Array ? [] : {});

    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }

        var val=src[key], sub;

        if(sel) {
            sub+=pth+"/"+key;
            if(!sel(sub,key,val)) { continue; }
            }

        if(val && typeof(val)=='object') {
            if     (val instanceof Boolean) { val=Boolean(val);        }
            else if(val instanceof Number ) { val=Number (val);        }
            else if(val instanceof String ) { val=String (val);        }
            else                            { val=_clone(sub,val,sel); }
            }
        ret[key]=val;
        }
    return ret;
    }

null以外のルートオブジェクトを想定し、メンバーを選択しない場合の最も単純で妥当なディープクローンソリューションは次のとおりです。

function clone(src) {
    var ret=(src instanceof Array ? [] : {});
    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }
        var val=src[key];
        if(val && typeof(val)=='object') { val=clone(val);  }
        ret[key]=val;
        }
    return ret;
    }

2

Los-Dashは、現在Underscore.jsのスーパーセットであり、ディープクローン機能がいくつかあります。

著者自身の答えから:

lodash underscore ビルドは、アンダースコアの最新の安定バージョンとの互換性を確保するために提供されています。


「ライブラリを使いたくない」という質問
Femi Oni

@FemiOni質問はライブラリについて何もありません(古い編集でも)...ここにある他のいくつかの回答も、ライブラリを使用しています。
CPHPython 2019年

@FemiOniの回答は昨日反対投票されました。とにかく、これは学習場所であり、誰かが実際にディープクローン自体を実装しようとしている場合に備えて、lodashソースコードbaseCloneはいくつかのアイデアを提供する可能性があります。
CPHPython

@FemiOni JSONオブジェクトはライブラリでもフレームワークでもありません...関数を実装する場合は、オープンソースライブラリの1つを調べ、必要なパーツを使用することをお勧めします(多くは何年もテストされています)。長期的には、バグや見落としを回避します。
CPHPython

2

以下の関数は、javascriptオブジェクトをディープクローンする最も効率的な方法です。

function deepCopy(obj){
    if (!obj || typeof obj !== "object") return obj;

    var retObj = {};

    for (var attr in obj){
        var type = obj[attr];

        switch(true){
            case (type instanceof Date):
                var _d = new Date();
                _d.setDate(type.getDate())
                retObj[attr]= _d;
                break;

            case (type instanceof Function):
                retObj[attr]= obj[attr];
                break;

            case (type instanceof Array):
                var _a =[];
                for (var e of type){
                    //_a.push(e);
                    _a.push(deepCopy(e));
                }
                retObj[attr]= _a;
                break;

            case (type instanceof Object):
                var _o ={};
                for (var e in type){
                    //_o[e] = type[e];
                    _o[e] = deepCopy(type[e]);
                }
                retObj[attr]= _o;
                break;

            default:
                retObj[attr]= obj[attr];
        }
    }
    return retObj;
}

var obj = {
    string: 'test',
    array: ['1'],
    date: new Date(),
    object:{c: 2, d:{e: 3}},
    function: function(){
        return this.date;
    }
};

var copyObj = deepCopy(obj);

console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true

2
あなたの主張を裏付ける議論はありますか?
Fabian von Ellerts

関数の下に例を示しました。疑問がありますか?
Kooldandy

これらの例は、関数が機能していることを示していますが、これはクールですが、なぜ「最も効率的な方法」なのでしょうか。
Fabian von Ellerts

これは、オブジェクトの属性を単一ループで再帰的にコピーしているためです。また、日付、関数、オブジェクト、配列、数値、文字列もすべて適切に処理されます。他に方法はありますか?
Kooldandy

2

このような関数は、もはや現実には必要ありません。これは単なる学術的な興味です。

純粋に演習として、これはより機能的な方法です。ガードブロックをそこに追加すること提案したので、これは@tfmontagueの回答の拡張です。しかし、私がES6に強いられ、すべてのものを機能化しているように感じているのを見て、これが私の短気なバージョンです。配列をマッピングしてオブジェクトを削減する必要があるため、ロジックが複雑になりますが、変更は回避されます。

const cloner = (x) => {
    const recurseObj = x => (typeof x === 'object') ? cloner(x) : x
    const cloneObj = (y, k) => {
        y[k] = recurseObj(x[k])
        return y
    }
    // Guard blocks
    // Add extra for Date / RegExp if you want
    if (!x) {
        return x
    }
    if (Array.isArray(x)) {
        return x.map(recurseObj)
    }
    return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
    null,
    [],
    {},
    [1,2,3],
    [1,2,3, null],
    [1,2,3, null, {}],
    [new Date('2001-01-01')], // FAIL doesn't work with Date
    {x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
    {
        obj : new function() {
            this.name = "Object test";
        }
    } // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))


1

Mapには特別な扱いが必要であることに気付いたので、このスレッドのすべての提案を使用すると、コードは次のようになります。

function deepClone( obj ) {
    if( !obj || true == obj ) //this also handles boolean as true and false
        return obj;
    var objType = typeof( obj );
    if( "number" == objType || "string" == objType ) // add your immutables here
        return obj;
    var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
    if( obj instanceof Map )
        for( var key of obj.keys() )
            result.set( key, deepClone( obj.get( key ) ) );
    for( var key in obj )
        if( obj.hasOwnProperty( key ) )
            result[key] = deepClone( obj[ key ] );
    return result;
}

1

すべての答えへの私の追加

deepCopy = arr => {
  if (typeof arr !== 'object') return arr
  if(arr.pop) return [...arr].map(deepCopy)
  const copy = {}
  for (let prop in arr)
    copy[prop] = deepCopy(arr[prop])
  return copy
}

0

これは、配列、オブジェクト、およびプリミティブで機能します。2つの走査方法を切り替える二重再帰アルゴリズム:

const deepClone = (objOrArray) => {

  const copyArray = (arr) => {
    let arrayResult = [];
    arr.forEach(el => {
        arrayResult.push(cloneObjOrArray(el));
    });
    return arrayResult;
  }

  const copyObj = (obj) => {
    let objResult = {};
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        objResult[key] = cloneObjOrArray(obj[key]);
      }
    }
    return objResult;
  }

  const cloneObjOrArray = (el) => {
    if (Array.isArray(el)) {
      return copyArray(el);
    } else if (typeof el === 'object') {
      return copyObj(el);
    } else {
      return el;
    }
  }

  return cloneObjOrArray(objOrArray);
}

0

再帰を利用して、deepCopyを作成できます。配列、オブジェクト、オブジェクトの配列、関数付きオブジェクトのコピーを作成できます。必要に応じて、マップなどの他のタイプのデータ構造の関数を追加できます。

function deepClone(obj) {
         var retObj;
        _assignProps = function(obj, keyIndex, retObj) {
               var subType = Object.prototype.toString.call(obj[keyIndex]);
               if(subType === "[object Object]" || subType === "[object Array]") {
                    retObj[keyIndex] = deepClone(obj[keyIndex]);
               }
               else {
                     retObj[keyIndex] = obj[keyIndex];
               }
        };

        if(Object.prototype.toString.call(obj) === "[object Object]") {
           retObj = {};
           for(key in obj) {
               this._assignProps(obj, key, retObj);
           }
        }
        else if(Object.prototype.toString.call(obj) == "[object Array]") {
           retObj = [];
           for(var i = 0; i< obj.length; i++) {
              this._assignProps(obj, i, retObj);
            }
        };

        return retObj;
    };

0

immutableJSを使用する

import { fromJS } from 'immutable';

// An object we want to clone
let objA = { 
   a: { deep: 'value1', moreDeep: {key: 'value2'} } 
};

let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object

console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }

// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false

またはlodash / merge

import merge from 'lodash/merge'

var objA = {
    a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA ); 

// We can also create new object from several objects by deep merge:
var objB = {
    a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }

0

これは循環参照を使用して、私のために働きます

 //a test-object with circular reference :
 var n1 = {   id:0,   text:"aaaaa",   parent:undefined} 
 var n2 = {  id:1,   text:"zzzzz",   parent:undefined } 
 var o = { arr:[n1,n2],   parent:undefined } 
 n1.parent = n2.parent = o;
 var obj = {   a:1,   b:2,   o:o }
 o.parent = obj;

 function deepClone(o,output){ 

     if(!output) output = {};  
     if(o.______clone) return o.______clone;
     o.______clone = output.______clone = output;

   for(var z in o){

     var obj = o[z];
     if(typeof(obj) == "object") output[z] = deepClone(obj)
     else output[z] = obj; 
    }

   return output;
}

console.log(deepClone(obj));

0

var newDate = new Date(this.oldDate); 私はoldDateを関数に渡してthis.oldDateからnewDateを生成していましたが、this.oldDateも変更していたため、そのソリューションを使用して機能しました。


0

このソリューションは、[... target]または{... target}を使用する場合の再帰の問題を回避します

function shallowClone(target) {
  if (typeof a == 'array') return [...target]
  if (typeof a == 'object') return {...target}
  return target
}

/* set skipRecursion to avoid throwing an exception on recursive references */
/* no need to specify refs, or path -- they are used interally */
function deepClone(target, skipRecursion, refs, path) {
  if (!refs) refs = []
  if (!path) path = ''
  if (refs.indexOf(target) > -1) {
    if (skipRecursion) return null
    throw('Recursive reference at ' + path)
  }
  refs.push(target)
  let clone = shallowCopy(target)
  for (i in target) target[i] = deepClone(target, refs, path + '.' + i)
  return clone
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.