web-dev-qa-db-ja.com

JavaScriptのネストされたオブジェクトの配列のディープコピー

JavaScriptでネストされたオブジェクトの配列をディープコピーしようとしています。私の配列はこのようになります

var arr = [{name:"adam",age:"21"},
    {name:"freddie",age:"35",children:[{name:"mercury",age:"25"}]},
    {name:"jim",age:"35",children:[{name:"morrison",age:"25",children:[{name:"some", age:"40"}]}]}
    ];

私は、配列内のすべてのオブジェクトの詳細なコピーを作成したいです。つまり、arrの正確なコピーを、オブジェクト参照を持たない新しい配列に作成したいと考えています。配列の深さも不明です。つまり、子配列は任意のレベルにすることができます。私はこのリンクを通過しました javascript(Deep copy) のオブジェクト参照なしでオブジェクトの配列を別の配列にコピーしています==しかし、それは私を助けませんでした。私はググってjQueryでいくつかの解決策を見つけましたが、jQueryの知識がないのでそれは私を助けませんでした。

私は再帰的にそれを実装しようとしましたが、それもうまくいきません http://ideone.com/kJi5X

JQueryなどを使用せずに、JavaScriptでのみ実行したい。私はJavaScriptを初めて使用するので、これを行うためのライブラリーまたは単純なメソッドがあると、見落としている可能性があります。この問題の解決を手伝ってください。前もって感謝します。

21
user2912611

主なオプションは2つあります。

  1. 使用する JSON.stringifyおよびJSON.parse

    var copy = JSON.parse(JSON.stringify(original));
    

    しかし私はそれが好きではありません。テキストのラウンドトリップはせいぜい非効率的であり、置換およびリバイバーを作成しない限り、DateRegExpundefinedなどの値を正しく処理しません。 。

  2. 次のような再帰関数を使用します。

var toString = Object.prototype.toString;
function deepCopy(obj) {
    var rv;

    switch (typeof obj) {
        case "object":
            if (obj === null) {
                // null => null
                rv = null;
            } else {
                switch (toString.call(obj)) {
                    case "[object Array]":
                        // It's an array, create a new array with
                        // deep copies of the entries
                        rv = obj.map(deepCopy);
                        break;
                    case "[object Date]":
                        // Clone the date
                        rv = new Date(obj);
                        break;
                    case "[object RegExp]":
                        // Clone the RegExp
                        rv = new RegExp(obj);
                        break;
                    // ...probably a few others
                    default:
                        // Some other kind of object, deep-copy its
                        // properties into a new object
                        rv = Object.keys(obj).reduce(function(prev, key) {
                            prev[key] = deepCopy(obj[key]);
                            return prev;
                        }, {});
                        break;
                }
            }
            break;
        default:
            // It's a primitive, copy via assignment
            rv = obj;
            break;
    }
    return rv;
}
var a = [1, {foo: "bar"}, ['a', 'b'], new Date()];
snippet.log(JSON.stringify(a));
var b = deepCopy(a);
snippet.log(JSON.stringify(b));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

上記では、すべての最新のブラウザーにあるES5機能を使用していますが、IE8などの一部の古いブラウザーは使用していません。上記のすべての機能は、古いブラウザではポリフィルできます。

カスタムコンストラクター関数を処理したり、配列内のオブジェクトのプロトタイプを保持したりすることはありません。そうすることで物事が劇的により複雑になり、コピー操作のためにこれらのコンストラクターを呼び出す方法に関する規則がないと、完璧にすることは不可能になります。同じプロトタイプを割り当てることで近づくことができますが、コンストラクター関数内のロジック、特にコンストラクター内でクロージャーとして設定された関数は考慮されません。

34
T.J. Crowder