JavaScriptでオブジェクトをディープクローンする最も効率的な方法は何ですか?


5180

JavaScriptオブジェクトを複製する最も効率的な方法は何ですか?obj = eval(uneval(o));使用されているのを見てきましたが、これは非標準であり、Firefoxでのみサポートされています。

私は次のようなことをしましたobj = JSON.parse(JSON.stringify(o));が、効率に疑問があります。

また、さまざまな欠陥がある再帰的なコピー機能を見てきました。
標準的な解決策が存在しないことに驚いています。


566
評価は悪ではありません。evalの使用は不十分です。あなたがその副作用を恐れているなら、あなたはそれを間違って使っています。あなたが恐れる副作用はそれを使う理由です。ところで、実際にあなたの質問に答えた人はいましたか?
James

15
オブジェクトのクローン作成は、特に任意のコレクションのカスタムオブジェクトを使用する場合、トリッキーなビジネスです。これがおそらく、すぐに使用できる方法がない理由です。
b01 2013年

12
eval()介して設定された変数を処理する場合eval多くのJavaScriptエンジンのオプティマイザをオフにする必要があるため、これは一般に悪い考えです。eval()コード内にあるだけでは、パフォーマンスが低下する可能性があります。
user56reinstatemonica8 2014


12
このJSONメソッドは、JSONに対応するものがないJavascriptタイプを失うことに注意してください。例:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))生成される{a: null, b: null, c: null, g: false}
oriadam 2017年

回答:


4731

ネイティブディープクローニング

これは「構造化クローニング」と呼ばれ、実験的にNode 11以降で動作し、うまくいけばブラウザーに到達するでしょう。詳細については、この回答を参照してください。

データ損失を伴う高速クローニング-JSON.parse / stringify

あなたが使用しない場合Dateの、機能、undefinedInfinityあなたのオブジェクト内の、正規表現、地図、セット、ブロブ、ファイルリスト、ImageDatas、スパース配列、型指定された配列または他の複合型を、深いクローンに非常に単純な1ライナーは、オブジェクトは、次のとおりです。

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'
  re: /.*/,  // lost
}
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()

ベンチマークについては、Corbanの回答を参照してください。

ライブラリを使用した信頼性の高いクローニング

オブジェクトのクローンは簡単ではない(複雑なタイプ、循環参照、関数など)ので、ほとんどの主要なライブラリは、オブジェクトをクローンする関数を提供します。ホイールを再発明しないでください。すでにライブラリを使用している場合は、オブジェクトのクローン作成機能があるかどうかを確認してください。例えば、

ES6

:完全を期すため、ノートES6は2つの浅いコピーメカニズム提供することObject.assign()スプレッドの構文を。これは、列挙可能なすべてのプロパティの値を1つのオブジェクトから別のオブジェクトにコピーします。例えば:

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax

7
@ThiefMaster github.com/jquery/jquery/blob/master/src/core.jsの 276行目(何か他のことをするコードが少しありますが、「JSでこれを行う方法」のコードがあります:)
Rune FS

7
興味のある方のために、jQueryディープコピーの背後にあるJSコードを以下に示します。github.com
Alex W

194
わあ!非常に明確にするために:この応答が正しい答えとして選ばれた理由がわからない、これは以下に与えられた応答への返信でした:stackoverflow.com/a/122190/6524(これは推奨さ.clone()れていましたが、適切なコードではありません)このコンテキストでの使用)。残念ながら、この質問は非常に多くの改訂を経ており、元の議論はもはや明白でさえありません!速度を重視する場合は、Corbanのアドバイスに従ってループを作成するか、プロパティを新しいオブジェクトに直接コピーしてください。または自分で試してみてください!
John Resig 14年

9
これはJavaScriptの質問です(jQueryについての言及はありません)。
gphilip 2015年

60
jQueryを使用せずにこれを行うにはどうすればよいですか?
Awesomeness01 2015

2266

このベンチマークを確認してください:http : //jsben.ch/#/bWfk9

私の以前のテストでは、速度が主な問題でした

JSON.parse(JSON.stringify(obj))

オブジェクトディープクローンと最も遅い方法であることが(それはより遅いjQuery.extenddeepフラグ10〜20%でtrueに設定)。

deepフラグがfalse(浅いクローン)に設定されている場合、jQuery.extendはかなり高速です。これは、型の検証のための追加のロジックが含まれ、未定義のプロパティなどをコピーしないため、適切なオプションですが、これによっても少し速度が低下します。

複製しようとしているオブジェクトの構造がわかっている場合、またはネストされた深い配列を回避できる場合は、for (var i in obj)hasOwnPropertyを確認しながらオブジェクトを複製する単純なループを作成できます。jQueryよりもはるかに高速です。

最後に、ホットループで既知のオブジェクト構造のクローンを作成しようとしている場合は、クローンプロシージャをインライン化してオブジェクトを手動で作成するだけで、はるかに高いパフォーマンスを得ることができます。

JavaScriptトレースエンジンはfor..inループの最適化に苦労し、hasOwnPropertyをチェックすると速度も低下します。速度が絶対必要な場合の手動クローン。

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

オブジェクトでこのJSON.parse(JSON.stringify(obj))メソッドを使用する場合は注意してください-ISO形式で日付の文字列表現を返しますが、Dateオブジェクトに変換さJSON.stringify(new Date())JSON.parse() ませんDate詳細については、この回答を参照してください

さらに、少なくともChrome 65では、ネイティブクローニングは適切ではありません。JSPerfによると、新しい関数を作成することによって、ネイティブのクローニングを行うことがほとんどである800X信じられないほど速いボード上のすべての方法であるJSON.stringifyを使用するよりも遅いです。

ES6のアップデート

Javascript ES6を使用している場合は、このネイティブな方法で複製または浅いコピーを試してください。

Object.assign({}, obj);

4
@trysis Object.createはオブジェクトのクローンを作成せず、プロトタイプオブジェクトを使用しています... jsfiddle.net/rahpuser/yufzc1jt/2
rahpuser

105
また、このメソッドは、削除されますkeys、あなたからobject、持っているfunctionsので、その値としてJSONの機能をサポートしていません。
Karlen Kishmiryan

39
またJSON.parse(JSON.stringify(obj))、日付オブジェクトで使用すると、日付がISO8601形式の文字列表現でUTCに変換されることにも注意してください
dnlgmzddr

31
JSONアプローチは、循環参照を窒息させます。
リッチリマー2016

28
@velop、Object.assign({}、objToClone)は、浅いクローンを作成しているように見えますが、開発ツールコンソールで遊んでいるときにそれを使用すると、オブジェクトクローンはまだクローンオブジェクトの参照をポイントしています。したがって、ここでは実際に適用できるとは思いません。
ギャレットシンプソン

473

オブジェクトに関数がなく、変数しかない場合は、次のように使用できます。

var newObject = JSON.parse(JSON.stringify(oldObject));

86
あなたのオブジェクトが文字列化する場合、これらが失われ、その後の任意の関数を(私は内部ゲッター&セッターを持っている)を持っていればそれはあなたがこの方法を必要とするすべてだ場合、私はちょうど見つけたとして、このアプローチの詐欺が...罰金さ..です
Markive

31
@Jason、このメソッドが(ディープオブジェクトでの)浅いコピーよりも遅い理由は、このメソッドが定義上、ディープコピーであるためです。ただし、JSONはネイティブコードで実装されているため(ほとんどのブラウザー)、これは他のJavaScriptベースのディープコピーソリューションを使用するよりもかなり高速であり、JavaScriptベースの浅いコピーテクニックよりも高速になる場合があります(jsperf.com/cloningを参照)-an-object / 79)。
MiJyn 2013

35
JSON.stringify({key: undefined}) //=> "{}"
Web_Designer 2014

32
この手法はDate、オブジェクト内に格納されているすべてのオブジェクトも破棄し、それらを文字列形式に変換します。
fstab 2014

13
JSON仕様(json.org)の一部ではないものはコピーできません
cdmckay

397

構造化クローニング

HTML標準には、 オブジェクトの深いクローンを作成できる内部構造化クローニング/シリアライゼーションアルゴリズムています。それはまだ特定の組み込み型に制限されていますが、JSONでサポートされているいくつかの型に加えて、日付、RegExps、マップ、セット、Blobs、FileLists、ImageDatas、スパース配列、型付き配列などもサポートします。 。また、クローンされたデータ内の参照を保持し、JSONのエラーの原因となる循環的かつ再帰的な構造をサポートできるようにします。

Node.jsでのサポート:試験運用版🙂

v8現在Node.js のモジュール(Node 11以降)は構造化されたシリアル化APIを直接公開していますが、この機能は「実験的」としてマークされており、将来のバージョンでは変更または削除される可能性があります。互換性のあるバージョンを使用している場合、オブジェクトの複製は次のように簡単です。

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

ブラウザーでの直接サポート:最終的には?😐

ブラウザーは現在、構造化クローニングアルゴリズムに直接インターフェースを提供していませんが、グローバルstructuredClone()関数についてはGitHubのwhatwg / html#793で説明されています。現在提案されているように、ほとんどの目的で使用するのは次のように簡単です。

const clone = structuredClone(original);

これが出荷されない限り、ブラウザーの構造化クローン実装は間接的にのみ公開されます。

非同期の回避策:使用可能。😕

既存のAPIで構造化クローンを作成するためのオーバーヘッドの少ない方法は、MessageChannelsの 1つのポートを介してデータをポストすることです。他のポートはmessage、アタッチされたの構造化されたクローンでイベントを発行し.dataます。残念ながら、これらのイベントをリッスンすることは必然的に非同期であり、同期の代替手段はあまり実用的ではありません。

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

使用例:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

同期の回避策:ひどい!🤢

構造化クローンを同期的に作成するための適切なオプションはありません。代わりに、実用的でないハックをいくつか示します。

history.pushState()そして、history.replaceState()の両方が彼らの最初の引数の構造化クローンを作成し、にその値を割り当てますhistory.state。これを使用して、次のようなオブジェクトの構造化されたクローンを作成できます。

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

使用例:

これは同期ですが、非常に遅くなる可能性があります。ブラウザの履歴の操作に関連するすべてのオーバーヘッドが発生します。このメソッドを繰り返し呼び出すと、Chromeが一時的に応答しなくなる可能性があります。

Notificationコンストラクタは、それに関連するデータの構造化クローンを作成します。また、ユーザーにブラウザ通知を表示しようとしますが、通知権限を要求しない限り、通知なしで失敗します。他の目的で許可を得ている場合は、作成した通知をすぐに閉じます。

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

使用例:


3
@rynah仕様をもう一度調べたところ、あなたの言うとおりです。メソッドhistory.pushState()history.replaceState()メソッドの両方history.stateが、最初の引数の構造化クローンに同期的に設定されています。少し変ですが、動作します。私は今答えを更新しています。
Jeremy Banks

40
これは本当に間違っています!そのAPIはこの方法で使用することを意図していません。
Fardin K. 14

209
FirefoxにpushStateを実装した人として、私はこのハックに対するプライドと嫌悪感の奇妙な組み合わせを感じています。よくやった、みんな。
ジャスティンL.

pushStateまたは通知ハックは、Functionなどの一部のオブジェクトタイプでは機能しません
Shishir Arora

323

組み込みのものがない場合は、次のことを試すことができます。

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

20
JQueryソリューションはDOM要素で機能しますが、オブジェクトだけでは機能しません。Mootoolsにも同じ制限があります。彼らがどんなオブジェクトに対しても一般的な「クローン」を持っていることを望みます... それはおそらく進むべき道です。
jschrab 2008

5
この関数は、複製されるオブジェクトにパラメーターを必要とするコンストラクターがある場合は機能しません。「var temp = new Object()」に変更して、すべてのケースで機能させることができるようです。
Andrew Arnott、

3
Andrew、これをvar temp = new Object()に変更すると、クローンには元のオブジェクトと同じプロトタイプがありません。使用してみてください: 'var newProto = function(){}; newProto.prototype = obj.constructor; var temp = new newProto(); '
limscoder

1
limscoderの回答と同様に、コンストラクターを呼び出さずにこれを行う方法については、以下の私の回答を参照してください:stackoverflow.com/a/13333781/560114
Matt Browne

3
サブパーツへの参照を含むオブジェクト(オブジェクトのネットワークなど)の場合、これは機能しません。2つの参照が同じサブオブジェクトを指している場合、コピーには2つの異なるコピーが含まれます。そして、再帰的な参照がある場合、関数は終了しません(少なくとも、希望する方法ではありません:-)これらの一般的なケースでは、すでにコピーされたオブジェクトのディクショナリを追加し、すでにコピーしたかどうかを確認する必要があります...単純な言語を使用するとプログラミングが複雑になる
virtualnobi '11 / 11/13

153

オブジェクトを1行のコードで(ディープクローンではなく)クローンする効率的な方法

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;
    }
  });
}

82
これは再帰的にコピーしないので、オブジェクトのクローンの問題に対する解決策を実際には提供しません。
mwhite 2016年

5
この方法は機能しましたが、いくつかのテストを行ったところ、_。extend({}、(obj))はBY FARが最速でした。たとえば、JSON.parseより20倍高速で、Object.assignより60%高速です。すべてのサブオブジェクトを非常によくコピーします。
ニコ、

11
@mwhiteは、クローンとディープクローンに違いがあります。この回答は実際にはクローンを作成しますが、ディープクローンは作成しません。
Meirion Hughes

57
opはディープクローンを要求しました。これはディープクローンを行いません。
user566245 2016

9
このメソッドは、DEEPコピーではなくSHALLOWコピーを作成します。このため、それは完全に間違った答えです!
Bharata

97

コード:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
        to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
}

テスト:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);

3
どの程度var obj = {}obj.a = obj
neaumusic

5
この機能がわかりません。仮定from.constructorであるDate例えば。if2番目のifテストが成功し、関数が戻るようになると、3番目のテストにどのように到達しDate != Object && Date != Arrayますか?
アダムマッキー

1
@AdamMcKee javascriptの引数の受け渡しと変数の割り当てには注意が必要です。このアプローチは、日付(実際には2番目のテストで処理されます)を含め、うまく機能します-ここでテストするのは簡単です:jsfiddle.net/zqv9q9c6
brichins

1
@NickSweeting:試してください-うまくいくかもしれません。そうでない場合-修正して、回答を更新してください。これが、コミュニティでの動作方法です。)
カマレイ

1
この関数は、テストで正規表現のクローンを作成しません。条件「from.constructor!= Object && from.constructor!= Array」は、Number、Dateなどの他のコンストラクターでは常にtrueを返します。
aMarCruz 2018年

95

これは私が使っているものです:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

8
これは正しくないようです。cloneObject({ name: null })=>{"name":{}}
ニヤズ2013

13
これは、JavaScriptの別のばかげたことによるものですtypeof null > "object"Object.keys(null) > TypeError: Requested keys of a value that is not an object.、状態をif(typeof(obj[i])=="object" && obj[i]!=null)
Vitim.us

これは、objの継承された列挙可能なプロパティを直接クローンに割り当て、objがプレーンオブジェクトであると想定します。
RobG 2016

これは、数値キーを持つオブジェクトに変換される配列を混乱させます。
ブレード

nullを使用しない場合は問題ありません。
ホルヘブカラン2017年

78

パフォーマンス別のディープコピー: 最高から最低までランク付け

  • 再割り当て "="(文字列配列、数値配列-のみ)
  • スライス(文字列配列、数値配列-のみ)
  • 連結(文字列配列、数値配列-のみ)
  • カスタム関数:forループまたは再帰的コピー
  • jQueryの$ .extend
  • JSON.parse(文字列配列、数値配列、オブジェクト配列-のみ)
  • Underscore.jsの_.clone(文字列配列、数値配列-のみ)
  • ローダッシュの_.cloneDeep

文字列または数値の配列をディープコピーします(1レベル-参照ポインターなし):

配列に数値と文字列が含まれている場合-.slice()、. concat()、. splice()などの関数、代入演算子「=」、およびUnderscore.jsのクローン関数。配列の要素の深いコピーを作成します。

再割り当てのパフォーマンスが最も速い場合:

var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];

そして.slice())(.concatよりも優れた性能を持っている、 http://jsperf.com/duplicate-array-slice-vs-concat/3

var arr1 = ['a', 'b', 'c'];  // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0);   // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat();   // Becomes arr2b = ['a', 'b', 'c'] - deep copy

オブジェクトの配列をディープコピーします(2つ以上のレベル-参照ポインター):

var arr1 = [{object:'a'}, {object:'b'}];

カスタム関数を記述します($ .extend()やJSON.parseよりも高速です):

function copy(o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
   }
   return out;
}

copy(arr1);

サードパーティのユーティリティ関数を使用します。

$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash

jQueryの$ .extendの方がパフォーマンスが良い場合:


私はいくつかテストしましたが、_。extend({}、(obj))はBY FARが最速でした。たとえば、JSON.parseより20倍高速で、Object.assignより60%高速です。すべてのサブオブジェクトを非常によくコピーします。
ニコ

4
あなたの例はすべて浅い、1レベルです。これは良い答えではありません。問題はディープクローニング、つまり少なくとも2つのレベルに関するものでした。
Karl Morrison

1
ディープコピーとは、他のオブジェクトへの参照ポインタを使用せずに、オブジェクト全体をコピーすることです。jQuery.extend()などの「オブジェクトの配列のディープコピー」セクションの手法や、カスタム関数(再帰的)は、「少なくとも2つのレベル」でオブジェクトをコピーします。したがって、すべての例が「1レベル」のコピーであるとは限りません。
tfmontague 2017

1
私はカスタムコピー機能が好きですが、null値を除外する必要があります。そうしないと、すべてのnull値がオブジェクトに変換されます。つまり、out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
josi

2
@HossamMourad-バグはJosiによって(上記のコメントで)2月1日に修正され、回答を正しく更新できませんでした。このバグが原因でコードベースがリファクタリングされて申し訳ありません。
tfmontague 2018年

64
var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});

良い答えですが、循環参照ではこれは失敗します。
ルーク

59

JavaScriptでのオブジェクトのディープコピー(最良かつ最も単純だと思います)

1. JSON.parse(JSON.stringify(object));を使用します。

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2.作成されたメソッドの使用

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Lo-Dashの_.cloneDeepリンクlodashを使用する

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Object.assign()メソッドの使用

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

しかし、間違っているとき

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5.Underscore.js _.cloneリンクの使用Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

しかし、間違っているとき

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

JSBEN.CH Performance Benchmarking Playground 1〜3 http://jsben.ch/KVQLd JavaScriptでのオブジェクトのディープコピーのパフォーマンス


5
Object.assign()ディープコピーを実行しない
Roymunson '15

1
これらのベンチマークを追加する必要があります。それは非常に役に立ちます
jcollum 2018年

配列を含むオブジェクトで「作成されたメソッド」を使用したときに、pop()またはsplice()を使用できませんでした。なぜでしょうか。let data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();それはスロー:TypeError: tmp.title.pop is not a function(ちょうど私場合は罰金を作品)(ポップもちろんdo let tmp = data、しかし、私はデータに影響を与えることなくTMP変更することはできません)
hugogogo

ねえ、あなたの最後の例は間違っています。私の意見では、間違った例では_cloneDeepではなく_cloneを使用する必要があります。
ケナニルディズ

この作成されたメソッド(2.)は配列では機能しませんか?
ToivoSäwén19年

57

あります(「クローン」と呼ばれる)ライブラリ非常によくこれを行います。これは、私が知っている任意のオブジェクトの最も完全な再帰的な複製/コピーを提供します。また、循環参照もサポートしていますが、これは他の回答ではまだカバーされていません。

npmでも見つけることができます。ブラウザだけでなくNode.jsにも使用できます。

これを使用する方法の例を次に示します。

でインストール

npm install clone

またはEnderでパッケージ化します。

ender build clone [...]

ソースコードを手動でダウンロードすることもできます。

その後、ソースコードで使用できます。

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(免責事項:私はライブラリの作成者です。)


3
npm cloneは、任意にネストされたオブジェクトを複製するために私にとって非常に貴重です。これが正解です。
Andy Ray

言うと比較してあなたのlibのパフォーマンスは何JSON.parse(JSON.stringify(obj))ですか?
pkyeck 2016

これは、より高速なオプションがあることを示すライブラリです。まだテストしていません。
pvorb 16

適切なソリューションであり、これは循環参照をサポートします(JSON解析とは異なります)
Luke

55

Cloning JSでは常にオブジェクトが問題でしたが、それはES6より前のことでした。JavaScriptでオブジェクトをコピーするさまざまな方法を以下にリストします。以下のオブジェクトがあり、それを深くコピーしたいとします。

var obj = {a:1, b:2, c:3, d:4};

原点を変更せずにこのオブジェクトをコピーする方法はいくつかあります。

1)ES5 +、簡単な関数を使用してコピーを実行します。

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    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 this object.");
}

2)JSON.parseおよびJSON.stringifyを使用したES5 +。

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3)AngularJs:

var  deepCopyObj = angular.copy(obj);

4)jQuery:

var deepCopyObj = jQuery.extend(true, {}, obj);

5)UnderscoreJs&Loadash:

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

これらの助けを願っています...


2
アンダースコアのクローンは、現在のバージョンではディープクローンではありません
Rogelio

ありがとう。アンダースコアの新しいドキュメントとしてはい... clone_.clone(object)提供されたプレーンオブジェクトの浅いコピーのクローンを作成します。ネストされたオブジェクトまたは配列は、複製ではなく参照によってコピーされます。_.clone({name: 'moe'}); => {name: 'moe'};
Alireza 2017

59
Object.assignディープコピーしませ。例:var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"。これがディープコピーだったy.a.bとしてもc、まだですが、今はそうdです。
kba

8
Object.assign()は、最初のレベルのプロパティのみを複製します。
haemse

5
cloneSO()関数とは何ですか?
pastorello 2017

53

私はこれが古い投稿であることを知っていますが、これはつまずく次の人に役立つかもしれないと思いました。

オブジェクトを何かに割り当てない限り、オブジェクトはメモリ内で参照を維持しません。したがって、他のオブジェクトと共有したいオブジェクトを作成するには、次のようなファクトリを作成する必要があります。

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

16
質問は次のとおりなので、この回答はあまり関係ありません。インスタンスbが与えられた場合、どのようにしてコピーを作成するかcファクトリaを知らないか、ファクトリaを使用したくない。ファクトリを使用したくない理由は、インスタンス化後、bが追加データ(ユーザー入力など)で初期化されている可能性があるためです。
ノエルアブラハム

12
これが本当の質問に対する答えではないのは事実ですが、ここに来ることが多くの人が本当に尋ねる意味があると私が思う質問に対する答えであるため、ここにいることが重要だと思います。
セミコロン2013年

8
申し訳ありませんが、なぜ多くの賛成票を投じたのかよくわかりません。オブジェクトのクローン作成はかなり明確な概念であり、ANOTHERオブジェクトからオブジェクトを円錐化します。ファクトリパターンで新しいオブジェクトを作成することとはあまり関係がありません。
opensas 2014

2
これは事前定義されたオブジェクトに対して機能しますが、この方法での「複製」は、元のオブジェクトに追加された新しいプロパティを認識しません。aを作成する場合は、aに新しいプロパティを追加してから、bを作成します。bには新しいプロパティはありません。基本的に、ファクトリパターンは新しいプロパティに対して不変です。これはパラダイム的にクローンを作成するものではありません。参照:jsfiddle.net/jzumbrun/42xejnbx
Jon

1
私はこれを使用const defaultFoo = { a: { b: 123 } };する代わりにあなたが行くことができconst defaultFoo = () => ({ a: { b: 123 } };、あなたの問題が解決されるので、私はこれは良いアドバイスだと思います しかし、それは質問に対する答えではありません。完全な答えではなく、質問に対するコメントとしてより意味があるかもしれません。
カリブーのジョシュ2017

48

使用している場合は、Underscore.jsライブラリにcloneメソッドがあります。

var newObject = _.clone(oldObject);

24
lodashにはcloneDeepメソッドがあり、それを深くするために複製する別のパラメーターもサポートしています:lodash.com/docs#cloneおよびlodash.com/docs#cloneDeep
opensas

12
@opensasは同意しました。ロダッシュは一般的にアンダースコアより優れています
nha 2014

7
私は、ユーティリティライブラリの.clone(...)メソッドへの1行の参照である、これと他のすべての回答を削除することを推奨します。すべての主要なライブラリにはそれらがあり、繰り返された短い詳細ではない回答は、その特定のライブラリを使用しないほとんどの訪問者には役立ちません。
Jeremy Banks

41

上記のConroyPの回答のバージョンを次に示します。これは、コンストラクターに必須パラメーターがある場合でも機能します。

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

この関数は、私のsimpleooライブラリでも使用できます。

編集:

これはより堅牢なバージョンです(Justin McCandlessのおかげで、これは循環参照もサポートするようになりました)。

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

30

以下は、同じオブジェクトの2つのインスタンスを作成します。見つけて、現在使用しています。シンプルで使いやすいです。

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

この答えに問題はありますか?スタンドアロンのソリューションでありながらシンプルなので、より便利です。しかし、jQueryソリューションの方が人気があります。何故ですか?
ceremcem 2015

はい、お知らせください。意図したとおりに機能しているようですが、どこかに隠れた破損がある場合は、別のソリューションを使用する必要があります。
ネイサンロジャース2015

4
単純なオブジェクトの場合、これはChromeでは指定された回答よりも約6倍遅く、オブジェクトの複雑さが増すにつれて遅くなります。それはひどくスケーリングし、アプリケーションを非常に迅速にボトルネックにする可能性があります。
tic

1
何が起こっているのかを理解するだけで、データは必要ありません。このクローン作成手法は、オブジェクト全体を文字列にシリアル化し、その文字列のシリアル化を解析してオブジェクトを構築します。本質的に、これは単にメモリを再配置するよりもはるかに遅くなります(これは、より高度なクローンが行うことです)。しかし、そうは言っても、小規模から中規模のプロジェクト(「中規模」の定義に依存します)の場合、1000倍も効率が悪いかどうかを気にしますか?オブジェクトが小さく、それらのクローンを作成しない場合、実質的に何もない1000倍のトンは、事実上何もありません。
machineghost 2016

3
また、このメソッドはメソッド(またはJSONで許可されていないもの)を失います。さらに、JSON.stringifyはDateオブジェクトを文字列に変換します...逆ではありません;)このソリューションは避けてください。
MT氏

22

Crockfordは、この関数を使用することをお勧めします(私も好みます)。

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

それは簡潔で、期待通りに動作し、ライブラリは必要ありません。


編集:

これはのポリフィルなObject.createので、これも使用できます。

var newObject = Object.create(oldObject);

注: これの一部を使用する場合、を使用する一部の反復で問題が発生する可能性がありますhasOwnPropertycreate継承する新しい空のオブジェクトを作成するためoldObjectです。ただし、オブジェクトのクローンを作成する場合は、依然として便利で実用的です。

たとえばの場合 oldObject.a = 5;

newObject.a; // is 5

だが:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

9
私が間違っている場合は私を修正しますが、プロトタイプ継承のためのクロックフォードのbeget関数ではありませんか?それはクローンにどのように適用されますか?
Alex Nolasco、

3
はい、私はこの議論を恐れていました:クローン、コピー、プロトタイプの継承の実際的な違いは何ですか、いつそれぞれを使用する必要があり、このページのどの関数が実際に何をしているのですか?「javascriptコピーオブジェクト」をグーグル検索してこのSOページを見つけました。私が本当に探していたのは上記の関数だったので、共有するために戻ってきました。私の推測では、質問者もこれを探していました。
Chris Broski、

51
clone / copyと継承の違いは、つまり、例を使用すると、oldObjectのプロパティを変更すると、newObjectでもプロパティが変更されます。コピーを作成すると、newObjectを変更せずにoldObjectを使用して必要な操作を実行できます。
Ridcully 2010

13
これにより、hasOwnPropertyチェックが無効になるため、オブジェクトを複製するかなりハックな方法で、予期しない結果が返されます。
コーバンブルック

var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };... davidshariff.com/blog/javascript-inheritance-patterns
Cody

22

Lodashには素敵な_.cloneDeep(value)メソッドがあります:

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

5
私は、ユーティリティライブラリの.clone(...)メソッドへの1行の参照である、これと他のすべての回答を削除することを推奨します。すべての主要なライブラリにはそれらがあり、繰り返された短い詳細ではない回答は、その特定のライブラリを使用しないほとんどの訪問者には役立ちません。
Jeremy Banks

より簡単な方法は、を使用すること_.merge({}, objA)です。lodashが最初にオブジェクトを変更しなかった場合、clone関数は必要ありません。
反乱2017

7
Google検索でJSオブジェクトのクローンを検索するには、こちらを参照してください。私はロダッシュを使用しているので、この答えは私に関連しています。答えにすべての「ウィキペディア削除主義者」を行かせないでください。
反乱2017

2
ノード9では、JSON.parse(JSON.stringify(arrayOfAbout5KFlatObjects))は_.deepClone(arrayOfAbout5KFlatObjects)よりもはるかに高速です。
Dan Dascalescu 2017

21
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }

17
メソッドの問題。obj内にサブオブジェクトがある場合、すべてのサブオブジェクトの値ではなく、それらの参照が複製されます。
カマレイ2009年

1
再帰的にするだけで、サブオブジェクトが深く複製されます。
フィアットジャフ2013年

ただ気になる...クローン変数は、元のオブジェクトのプロパティへのポインタを持っているのではないでしょうか?新しいメモリ割り当てがないようです
Rupesh Patel

3
はい。これは浅いコピーであるため、クローンは元のオブジェクトが指し示すまったく同じオブジェクトを指します。
Mark Cidade、2013

これは答えではありません。文字通り、オブジェクトに別のオブジェクトへの参照を詰め込むだけです。ソースオブジェクトを変更すると、「クローン」も変更されます。
Shawn Whinnery

19

浅いコピーのワンライナー(ECMAScript 5th edition):

var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

そして、浅いコピーのワンライナー(ECMAScript 6th edition、2015):

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

6
これは単純なオブジェクトでは問題ないかもしれませんが、プロパティ値のみをコピーします。プロトタイプチェーンには触れず、これを使用Object.keysすることにより、列挙不可能な継承されたプロパティをスキップします。また、直接割り当てを行うことにより、プロパティ記述子が失われます。
Matt Bierner 2013年

プロトタイプもコピーすると、列挙できないものとプロパティ記述子のみが欠落することになりますよね?かなり良い。:)
サム

パフォーマンスはさておき、これはオブジェクトを浅くコピーする本当に便利な方法です。私はよくこれを使用して、Reactコンポーネントの破壊的な割り当てで偽の休息プロパティを並べ替えます。
mjohnsonengr 2016年

17

AngularJSが言及されていないのを見て、人々が知りたいかもしれないと思ったので...

angular.copy オブジェクトと配列をディープコピーする方法も提供します。


またはjQieryは拡張と同じように使用される可能性がありますangular.extend({},obj);
ガルバニ

2
@Galvani:それはことに留意すべきであるjQuery.extendangular.extendの両方の浅いコピーです。angular.copy深いコピーです。
Dan Atkinson

16

配列のようなオブジェクトには、理想的なディープクローンオペレーターがまだないようです。以下のコードが示すように、John ResigのjQueryクローンは非数値プロパティを持つ配列を配列ではないオブジェクトに変換し、RegDwightのJSONクローンは非数値プロパティを削除します。次のテストは、複数のブラウザでこれらのポイントを示しています。

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)

14
他の人がResigの回答へのコメントで指摘したように、配列のようなオブジェクトを複製したい場合は、拡張呼び出しで{}を[]に変更します。例:jQuery.extend(true、[]、obj)
Anentropic

15

あなたの目的が「プレーンな古いJavaScriptオブジェクト」のクローンを作成することかどうかに応じて、2つの良い答えがあります。

また、ソースオブジェクトへのプロトタイプ参照がない完全なクローンを作成することを想定しています。完全なクローンに関心がない場合は、他のいくつかの回答で提供されているObject.clone()ルーチンの多くを使用できます(クロックフォードのパターン)。

プレーンな古いJavaScriptオブジェクトの場合、最新のランタイムでオブジェクトを複製するための実証済みの優れた方法は非常に簡単です。

var clone = JSON.parse(JSON.stringify(obj));

ソースオブジェクトは純粋なJSONオブジェクトでなければならないことに注意してください。つまり、ネストされたプロパティはすべてスカラー(ブール値、文字列、配列、オブジェクトなど)でなければなりません。RegExpやDateなどの関数や特別なオブジェクトは複製されません。

効率的ですか?はい、はい。私たちはあらゆる種類のクローニング方法を試しましたが、これが最も効果的です。忍者がもっと速い方法を思いつくかもしれないと私は確信している。しかし、限界利益について話しているのではないかと思います。

このアプローチは、シンプルで実装が簡単です。それを便利な関数にラップして、本当にいくつかのゲインを絞る必要がある場合は、後で行ってください。

さて、プレーンでないJavaScriptオブジェクトに対しては、本当に簡単な答えはありません。実際、JavaScript関数の動的な性質と内部オブジェクトの状態が原因ではあり得ません。内部に関数を含むJSON構造を詳細に複製するには、それらの関数とその内部コンテキストを再作成する必要があります。そしてJavaScriptには、標準化された方法がありません。

これを行う正しい方法は、宣言し、コード内で再利用する便利なメソッドを使用することです。便利なメソッドには、独自のオブジェクトをある程度理解させることができるため、新しいオブジェクト内でグラフを適切に再作成することができます。

私たちは独自に作成しましたが、私が見た中で最も一般的なアプローチはここでカバーされています:

http://davidwalsh.name/javascript-clone

これは正しい考えです。著者(David Walsh)は、一般化された関数の複製についてコメントアウトしました。ユースケースによっては、これを選択することもできます。

主なアイデアは、関数(またはプロトタイプクラス)のインスタンス化を型ごとに特別に処理する必要があるということです。ここでは、RegExpとDateの例をいくつか提供しています。

このコードは簡潔であるだけでなく、非常に読みやすくなっています。拡張は非常に簡単です。

これは効率的ですか?はい、はい。目標が真のディープコピークローンを作成することであることを考えると、ソースオブジェクトグラフのメンバーをウォークする必要があります。このアプローチでは、処理する子メンバーとカスタムタイプを手動で処理する方法を正確に調整できます。

さあ、行きます。2つのアプローチ。どちらも私の見解では効率的です。


13

これは一般的に最も効率的なソリューションではありませんが、私が必要とするものを実行します。以下の簡単なテストケース...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

巡回配列テスト...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

機能テスト...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false

11

AngularJS

まあ、あなたが角度を使っているなら、あなたもこれを行うことができます

var newObject = angular.copy(oldObject);

11

私はここで最も多くの票を獲得した答えに同意しません。再帰ディープクローンであるはるかに高速よりJSON.parse(JSON.stringify(OBJ))に述べたアプローチ。

  • ここでJsperfが 1位にランク付けしています。 https
  • 上記の回答のJsbenは、再帰的なディープクローンが他のすべての言及よりも優れていることを示すために更新されました。 http://jsben.ch/13YKQ

そして、ここにクイックリファレンス用の関数があります:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}

2
私はこのアプローチが好きですが、日付を適切に処理しません。if(o instanceof Date) return new Date(o.valueOf());nullをチェックした後などを追加することを検討してください`
Luis

循環参照でクラッシュします。
ハリー

最新の安定したFirefoxでは、これはそのJsben.chリンクの他の戦略よりも桁違いに長いです。それは間違った方向に他を打ちます。
WBT 2019年

11
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};

10

ECMAScript 6またはトランスパイラーを使用できる場合のみ

特徴:

  • コピー中にゲッター/セッターをトリガーしません。
  • ゲッター/セッターを保持します。
  • プロトタイプ情報を保持します。
  • オブジェクトリテラル機能的な オブジェクト指向の両方のライティングスタイルで動作します。

コード:

function clone(target, source){

    for(let key in source){

        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
        let descriptor = Object.getOwnPropertyDescriptor(source, key);
        if(descriptor.value instanceof String){
            target[key] = new String(descriptor.value);
        }
        else if(descriptor.value instanceof Array){
            target[key] = clone([], descriptor.value);
        }
        else if(descriptor.value instanceof Object){
            let prototype = Reflect.getPrototypeOf(descriptor.value);
            let cloneObject = clone({}, descriptor.value);
            Reflect.setPrototypeOf(cloneObject, prototype);
            target[key] = cloneObject;
        }
        else {
            Object.defineProperty(target, key, descriptor);
        }
    }
    let prototype = Reflect.getPrototypeOf(source);
    Reflect.setPrototypeOf(target, prototype);
    return target;
}

9

JavaScriptオブジェクトを複製できる包括的なclone()メソッドを次に示します。ほとんどすべてのケースを処理します。

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;
};

プリミティブをラッパーオブジェクトに変換しますが、ほとんどの場合は適切なソリューションではありません。
ダヌビアンセーラー2014

@DanubianSailor-私はそうは思わない...それは最初からプリミティブを返すようであり、それらが返されたときにそれらをラッパーオブジェクトに変えるような何もしていないようです。
Jimbo Jonny、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.