5173

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

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

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

4

67 に答える 67

5387

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

現在、「構造化クローン」と呼ばれるJS標準があります。これは、ノード11以降で実験的に機能し、ブラウザーに組み込まれ、既存のシステム用のポリフィルを備えています。

structuredClone(value)

必要に応じて、最初にポリフィルをロードします。

import structuredClone from '@ungap/structured-clone';

詳細については、この回答を参照してください。

古い答え

データ損失のある高速クローン作成-JSON.parse/stringify

Dateオブジェクト内でs、functions undefined、、、 RegExps、Maps、Sets、Blobs、FileLists、ImageDatas、sparse Arrays、Typed Arrays、またはその他の複雑な型を使用しない場合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'
  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の回答を参照してください。

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

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

  • lodash- cloneDeep; lodash.clonedeepモジュールを介して個別にインポートでき、ディープクローン機能を提供するライブラリをまだ使用していない場合はおそらく最良の選択です。
  • AngularJS-angular.copy
  • jQuery- jQuery.extend(true, { }, oldObject); .clone()DOM要素のみを複製します
  • ただのライブラリ- just-clone; 1つのことだけを行うゼロ依存npmモジュールのライブラリの一部。あらゆる機会に罪悪感のないユーティリティ。

ES6(浅いコピー)

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

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax
于 2008-09-23T18:09:37.277 に答える
2401

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

以前のテストでは、速度が主な関心事であることがわかりました

JSON.parse(JSON.stringify(obj))

オブジェクトをディープ クローンする最も遅い方法です (フラグが trueに設定されたjQuery.extenddeepよりも10 ~ 20% 遅くなります)。

フラグが(shallow clone)deepに設定されている場合、jQuery.extend は非常に高速です。false型検証のための追加のロジックが含まれており、未定義のプロパティなどをコピーしないため、これは良いオプションですが、これも少し遅くなります。

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

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

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

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

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

さらに、少なくとも Chrome 65 では、ネイティブ クローンは使用できないことに注意してください。JSPerf によると、新しい関数を作成してネイティブ クローンを実行すると、 JSON.stringify を使用するよりも800倍近く遅くなり、全体的に非常に高速です。

ES6 の更新

Javascript ES6 を使用している場合は、クローン作成または浅いコピーにこのネイティブ メソッドを試してください。

Object.assign({}, obj);
于 2011-03-17T19:19:55.880 に答える
545

構造化されたクローニング

2022アップデート:グローバルstructuredClone機能はFirefox 94、Node 17、Deno1.14ですでに利用可能です

HTML標準には、オブジェクトのディープクローンを作成できる内部構造化クローン作成/シリアル化アルゴリズムが含まれています。それでも特定の組み込み型に制限されていますが、JSONでサポートされるいくつかの型に加えて、Dates、RegExps、Maps、Sets、Blobs、FileLists、ImageDatas、sparse Arrays、Typed Arrays、およびおそらく将来的にはさらに多くの型もサポートします。また、複製されたデータ内の参照を保持し、JSONのエラーの原因となる循環的および再帰的な構造をサポートできるようにします。

Node.jsでのサポート:

structuredCloneグローバル関数はノード17.0によって提供されます。

const clone = structuredClone(original);

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

const v8 = require('v8');

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

ブラウザでの直接サポート:Firefox94で利用可能

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

使用例:

'use strict';

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

  const clone = structuredClone(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.");
};

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

main();

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

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

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

使用例:

'use strict';

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

  const clone = structuredClone(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.");
};

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

main();

于 2012-06-06T14:59:04.077 に答える
536

オブジェクトにプロパティのみがあり、関数がないと仮定すると、次を使用できます。

var newObject = JSON.parse(JSON.stringify(oldObject));
于 2011-01-04T08:05:55.877 に答える
359

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

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;
}
于 2008-09-23T16:38:51.143 に答える
114

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

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;
}
于 2009-12-11T22:47:19.153 に答える
95

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 パフォーマンス ベンチマーク プレイグラウンド 1~3 http://jsben.ch/KVQLd パフォーマンス JavaScript でのオブジェクトのディープ コピー

于 2018-08-08T08:17:31.580 に答える
67
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});
于 2009-12-26T14:59:51.560 に答える
62

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

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

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

于 2012-10-17T18:36:31.323 に答える
54

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

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

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
于 2011-09-24T19:28:11.733 に答える
48

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

var newObject = _.clone(oldObject);
于 2011-12-15T15:56:15.577 に答える
45

上記の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();
    };
}
于 2012-11-11T17:53:39.010 に答える
31

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

var objToCreate = JSON.parse(JSON.stringify(cloneThis));
于 2015-08-21T15:51:17.560 に答える
25

Crockfordは、この関数の使用を提案しています(そして私は好みます)。

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

var newObject = object(oldObject);

簡潔で、期待どおりに機能し、ライブラリは必要ありません。


編集:

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

var newObject = Object.create(oldObject);

注: これの一部を使用すると、を使用する反復で問題が発生する可能性がありますhasOwnProperty。なぜなら、createを継承する新しい空のオブジェクトを作成するからoldObjectです。ただし、オブジェクトのクローン作成には依然として有用で実用的です。

たとえば、 oldObject.a = 5;

newObject.a; // is 5

しかし:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
于 2010-10-06T15:08:59.540 に答える
23

Lodash には_.cloneDeep(value)メソッドがあります。

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

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
于 2013-06-22T15:03:59.290 に答える
22
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
于 2008-09-23T16:45:39.707 に答える
21

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

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 第 6 版、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
于 2012-07-04T21:44:26.990 に答える
18

配列のようなオブジェクトに対する理想的なディープ クローン オペレータはまだないようです。以下のコードが示すように、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)
于 2010-10-17T04:01:53.347 に答える
15

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

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
于 2011-04-03T02:08:03.017 に答える
14

バージョンを使用したいがJSON.parse(JSON.stringify(obj))、Date オブジェクトを失うことなく、メソッドの2 番目の引数をparse使用して、文字列を Date に戻すことができます。

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(obj), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v)
    return v;
  })
}

// usage:
var original = {
 a: [1, null, undefined, 0, {a:null}, new Date()],
 b: {
   c(){ return 0 }
 }
}

var cloned = clone(original)

console.log(cloned)

于 2015-10-29T16:09:55.147 に答える
14

ECMAScript 6またはtranspilersを使用できる場合のみ。

特徴:

  • コピー中にゲッター/セッターをトリガーしません。
  • ゲッター/セッターを保持します。
  • プロトタイプ情報を保持します。
  • オブジェクトリテラル関数型 オブジェクト指向の両方の記述スタイルで動作します。

コード:

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;
}
于 2015-10-22T04:05:41.490 に答える
13

ここで最大の票を得た回答には同意しません。Recursive Deep Cloneは、前述のJSON.parse(JSON.stringify(obj))アプローチよりもはるかに高速です。

クイックリファレンス用の関数は次のとおりです。

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
}
于 2017-06-18T06:34:37.290 に答える
12
// 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;
};
于 2010-04-28T11:16:55.327 に答える
12

AngularJS

angularを使用している場合は、これも行うことができます

var newObject = angular.copy(oldObject);
于 2016-09-14T13:26:15.817 に答える
12

これは、あらゆる 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;
};
于 2012-07-23T21:39:22.093 に答える
9

deepCopyJavaScript では、次のようにメソッドを記述できます。

function deepCopy(src) {
  let target = Array.isArray(src) ? [] : {};
  for (let prop in src) {
    let value = src[prop];
    if(value && typeof value === 'object') {
      target[prop] = deepCopy(value);
  } else {
      target[prop] = value;
  }
 }
    return target;
}
于 2018-11-05T09:43:14.203 に答える
7

npm クローン ライブラリを使用します。ブラウザでも動作するようです。

https://www.npmjs.com/package/clone

let a = clone(b)
于 2016-08-05T18:54:20.050 に答える
7

私の経験では、再帰バージョンは大幅に優れていJSON.parse(JSON.stringify(obj))ます。以下は、1 行に収まる最新の再帰的ディープ オブジェクト コピー関数です。

function deepCopy(obj) {
  return Object.keys(obj).reduce((v, d) => Object.assign(v, {
    [d]: (obj[d].constructor === Object) ? deepCopy(obj[d]) : obj[d]
  }), {});
}

これは、メソッドよりも約40 倍高速に実行されます。JSON.parse...

于 2018-06-19T22:06:13.553 に答える
6

単一行の ECMAScript 6 ソリューション (Date/Regex などの特殊なオブジェクト タイプは処理されません):

const clone = (o) =>
  typeof o === 'object' && o !== null ?      // only clone objects
  (Array.isArray(o) ?                        // if cloning an array
    o.map(e => clone(e)) :                   // clone each of its elements
    Object.keys(o).reduce(                   // otherwise reduce every key in the object
      (r, k) => (r[k] = clone(o[k]), r), {}  // and save its cloned value into a new object
    )
  ) :
  o;                                         // return non-objects as is

var x = {
  nested: {
    name: 'test'
  }
};

var y = clone(x);

console.log(x.nested !== y.nested);

于 2016-07-17T17:10:41.433 に答える
6

ES 2017 の例:

let objectToCopy = someObj;
let copyOfObject = {};
Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(objectToCopy));
// copyOfObject will now be the same as objectToCopy
于 2018-03-26T17:42:48.277 に答える
5

これは、私が作成したプロトタイプを使用しない最速のメソッドであるため、新しいオブジェクトで hasOwnProperty を維持します。

解決策は、元のオブジェクトの最上位のプロパティを繰り返し、2 つのコピーを作成し、元のオブジェクトから各プロパティを削除してから、元のオブジェクトをリセットして新しいコピーを返すことです。トップレベルのプロパティと同じ回数だけ反復する必要があります。ifこれにより、各プロパティが関数、オブジェクト、文字列などであるかどうかを確認するためのすべての条件が保存され、各子孫プロパティを反復する必要がなくなります。

唯一の欠点は、元のオブジェクトをリセットするために、元のオブジェクトに元の作成された名前空間を提供する必要があることです。

copyDeleteAndReset:function(namespace,strObjName){
    var obj = namespace[strObjName],
    objNew = {},objOrig = {};
    for(i in obj){
        if(obj.hasOwnProperty(i)){
            objNew[i] = objOrig[i] = obj[i];
            delete obj[i];
        }
    }
    namespace[strObjName] = objOrig;
    return objNew;
}

var namespace = {};
namespace.objOrig = {
    '0':{
        innerObj:{a:0,b:1,c:2}
    }
}

var objNew = copyDeleteAndReset(namespace,'objOrig');
objNew['0'] = 'NEW VALUE';

console.log(objNew['0']) === 'NEW VALUE';
console.log(namespace.objOrig['0']) === innerObj:{a:0,b:1,c:2};
于 2011-06-24T09:41:38.973 に答える
5

今後の参考のために、 ECMAScript 6の現在のドラフトでは、オブジェクトを複製する方法としてObject.assignが導入されています。コード例は次のとおりです。

var obj1 = { a: true, b: 1 };
var obj2 = Object.assign(obj1);
console.log(obj2); // { a: true, b: 1 }

これを書いている時点では、サポートはブラウザーの Firefox 34 に限定されているため、まだ製品コードでは使用できません (もちろん、Firefox 拡張機能を作成している場合を除きます)。

于 2014-08-24T21:09:50.147 に答える
5

多くの答えがありますが、どれも私が必要とする望ましい効果をもたらしませんでした. jQuery のディープ コピーの機能を利用したかったのですが、配列に実行されると、単に配列への参照をコピーし、その中の項目をディープ コピーします。これを回避するために、新しい配列を自動的に作成するちょっとした再帰関数を作成しました。

(必要に応じて kendo.data.ObservableArray もチェックします! ただし、配列を再び監視可能にする場合は、必ず kendo.observable(newItem) を呼び出してください。)

したがって、既存のアイテムを完全にコピーするには、次のようにします。

var newItem = jQuery.extend(true, {}, oldItem);
createNewArrays(newItem);


function createNewArrays(obj) {
    for (var prop in obj) {
        if ((kendo != null && obj[prop] instanceof kendo.data.ObservableArray) || obj[prop] instanceof Array) {
            var copy = [];
            $.each(obj[prop], function (i, item) {
                var newChild = $.extend(true, {}, item);
                createNewArrays(newChild);
                copy.push(newChild);
            });
            obj[prop] = copy;
        }
    }
}
于 2013-07-29T02:50:25.597 に答える
4

ES2015これは、デフォルト値とスプレッド演算子を使用してオブジェクトをディープクローンする私の方法です

 const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

const testObj = {
  "type": "object",
  "properties": {
    "userId": {
      "type": "string",
      "chance": "guid"
    },
    "emailAddr": {
      "type": "string",
      "chance": {
        "email": {
          "domain": "fake.com"
        }
      },
      "pattern": ".+@fake.com"
    }
  },
  "required": [
    "userId",
    "emailAddr"
  ]
}

const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

console.log(makeDeepCopy(testObj))

于 2017-10-15T20:01:27.260 に答える
3

を使用Object.create()してprototypeおよび のサポートを取得しinstanceof、ループを使用for()して列挙可能なキーを取得します。

function cloneObject(source) {
    var key,value;
    var clone = Object.create(source);

    for (key in source) {
        if (source.hasOwnProperty(key) === true) {
            value = source[key];

            if (value!==null && typeof value==="object") {
                clone[key] = cloneObject(value);
            } else {
                clone[key] = value;
            }
        }
    }

    return clone;
}
于 2015-06-19T02:56:44.930 に答える
3

class Handler {
  static deepCopy (obj) {
    if (Object.prototype.toString.call(obj) === '[object Array]') {
      const result = [];
      
      for (let i = 0, len = obj.length; i < len; i++) {
        result[i] = Handler.deepCopy(obj[i]);
      }
      return result;
    } else if (Object.prototype.toString.call(obj) === '[object Object]') {
      const result = {};
      for (let prop in obj) {
        result[prop] = Handler.deepCopy(obj[prop]);
      }
      return result;
    }
    return obj;
  }
}

于 2016-11-24T11:15:13.070 に答える
3

オブジェクトのクローン作成アルゴリズムを一般化する場合、これが最適なソリューションだと思います。
jQuery の有無にかかわらず使用できますが、複製されたオブジェクトに元のオブジェクトと同じ「クラス」を持たせたい場合は、jQuery の extends メソッドを除外することをお勧めします。

function clone(obj){
    if(typeof(obj) == 'function')//it's a simple function
        return obj;
    //of it's not an object (but could be an array...even if in javascript arrays are objects)
    if(typeof(obj) !=  'object' || obj.constructor.toString().indexOf('Array')!=-1)
        if(JSON != undefined)//if we have the JSON obj
            try{
                return JSON.parse(JSON.stringify(obj));
            }catch(err){
                return JSON.parse('"'+JSON.stringify(obj)+'"');
            }
        else
            try{
                return eval(uneval(obj));
            }catch(err){
                return eval('"'+uneval(obj)+'"');
            }
    // I used to rely on jQuery for this, but the "extend" function returns
    //an object similar to the one cloned,
    //but that was not an instance (instanceof) of the cloned class
    /*
    if(jQuery != undefined)//if we use the jQuery plugin
        return jQuery.extend(true,{},obj);
    else//we recursivley clone the object
    */
    return (function _clone(obj){
        if(obj == null || typeof(obj) != 'object')
            return obj;
        function temp () {};
        temp.prototype = obj;
        var F = new temp;
        for(var key in obj)
            F[key] = clone(obj[key]);
        return F;
    })(obj);            
}
于 2011-03-27T20:29:48.873 に答える
3

浅いコピーの場合、ECMAScript2018 標準で導入された優れたシンプルな方法があります。Spread Operatorの使用が含まれます。

let obj = {a : "foo", b:"bar" , c:10 , d:true , e:[1,2,3] };

let objClone = { ...obj };

Chrome ブラウザーでテストしましたが、両方のオブジェクトが別の場所に保存されているため、いずれかで直接の子の値を変更しても、他方は変更されません。(例では)値を変更すると、e両方のコピーに影響します。

このテクニックは非常にシンプルで簡単です。これは、この質問に対する真のベスト プラクティスだと思います。

于 2018-06-24T19:08:38.040 に答える
2

新しいブラウザが必要ですが...

ネイティブ オブジェクトを拡張して、実数 を取得しましょう.extend()

Object.defineProperty(Object.prototype, 'extend', {
    enumerable: false,
    value: function(){
        var that = this;

        Array.prototype.slice.call(arguments).map(function(source){
            var props = Object.getOwnPropertyNames(source),
                i = 0, l = props.length,
                prop;

            for(; i < l; ++i){
                prop = props[i];

                if(that.hasOwnProperty(prop) && typeof(that[prop]) === 'object'){
                    that[prop] = that[prop].extend(source[prop]);
                }else{
                    Object.defineProperty(that, prop, Object.getOwnPropertyDescriptor(source, prop));
                }
            }
        });

        return this;
    }
});

オブジェクトで .extend() を使用するコードの前にそれをポップするだけです。

例:

var obj1 = {
    node1: '1',
    node2: '2',
    node3: 3
};

var obj2 = {
    node1: '4',
    node2: 5,
    node3: '6'
};

var obj3 = ({}).extend(obj1, obj2);

console.log(obj3);
// Object {node1: "4", node2: 5, node3: "6"}
于 2015-08-04T19:38:46.340 に答える