web-dev-qa-db-ja.com

JavaScriptオブジェクトを正しく複製する方法

xというオブジェクトがあります。 yへの変更がyを変更しないように、それをオブジェクトxとしてコピーしたいです。組み込みのJavaScriptオブジェクトから派生したオブジェクトをコピーすると、余分な不要なプロパティが生成されることに気付きました。私は自分自身の、文字通りに構築されたオブジェクトの1つをコピーしているので、これは問題ではありません。

JavaScriptオブジェクトを正しく複製する方法

2754
mindeavor

JavaScriptで任意のオブジェクトに対してこれを実行するのは簡単でも簡単でもありません。あなたは誤ってプロトタイプに残っていて、新しいインスタンスにコピーされるべきではないオブジェクトのプロトタイプから属性を拾うという問題に出くわすでしょう。たとえば、いくつかの回答で示されているように、cloneメソッドをObject.prototypeに追加する場合は、その属性を明示的にスキップする必要があります。しかし、Object.prototypeや他の中間プロトタイプに追加のメソッドが追加されていて、あなたが知らないのならどうでしょう?その場合、あなたがすべきではない属性をコピーするので、 hasOwnProperty メソッドを使って予期しない、ローカルではない属性を検出する必要があります。

非列挙型の属性に加えて、隠しプロパティを持つオブジェクトをコピーしようとすると、さらに難しい問題に遭遇します。たとえば、prototypeは関数の隠しプロパティです。また、オブジェクトのプロトタイプは属性__proto__で参照されていますが、これも非表示になっており、ソースオブジェクトの属性を反復処理するfor/inループによってコピーされることはありません。 __proto__はFirefoxのJavaScriptインタプリタに固有のものであり、他のブラウザでは異なるものになる可能性がありますが、わかります。すべてが列挙可能というわけではありません。あなたがその名前を知っていればあなたは隠し属性をコピーすることができますが、私はそれを自動的に発見する方法を知りません。

洗練された解決策を探求するためのもう1つの問題は、プロトタイプ継承を正しく設定することの問題です。ソースオブジェクトのプロトタイプがObjectであれば、{}を使って新しい一般オブジェクトを作成するだけでうまくいきますが、ソースのプロトタイプがObjectの子孫である場合は、hasOwnPropertyを使用してスキップした追加フィルタ、またはプロトタイプにあったが、そもそも列挙できなかった。 1つの解決策は、ソースオブジェクトのconstructorプロパティを呼び出して初期コピーオブジェクトを取得してから属性をコピーオーバーすることですが、それでも列挙不可能な属性は取得できません。例えば、 Date オブジェクトはそのデータを隠しメンバーとして格納します。

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

d1の日付文字列は、d2の日付文字列より5秒遅れます。あるDateを他のものと同じにする方法はsetTimeメソッドを呼び出すことですが、それはDateクラスに固有のものです。私は間違っていることがうれしいですが、私はこの問題に対する防弾一般的な解決策があるとは思わない!

一般的なディープコピーを実装する必要があるときは、プレーンなObjectArrayDateStringNumber、またはBooleanのみをコピーすればよいと仮定して妥協しました。最後の3つのタイプは不変です、それで私は浅いコピーを実行することができて、それが変わるのを心配しませんでした。さらに、ObjectまたはArrayに含まれる要素も、このリストの6つの単純型のうちの1つになると思いました。これは次のようなコードで実現できます。

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

上記の関数は、オブジェクトと配列のデータがツリー構造を形成している限り、前述の6つの単純型に対して適切に機能します。つまり、オブジェクト内の同じデータへの参照は複数存在しません。例えば:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

JavaScriptオブジェクトを処理することはできませんが、あなたがそれをあなたがそれに投げたもののためだけに働くと仮定しない限り、それは多くの目的のために十分かもしれません。

1434
A. Levy

あなたがあなたのオブジェクトの中で関数を使用しないならば、非常に単純なワンライナーは以下のようになることができます:

var cloneOfA = JSON.parse(JSON.stringify(a));

これは、オブジェクト、配列、文​​字列、ブール値、数値を含むあらゆる種類のオブジェクトに対して機能します。

ブラウザの構造化クローンアルゴリズムに関するこの記事 を参照してください。/ /ワーカーとの間でメッセージを投稿するときに使用されます。ディープクローニングのための機能も含まれています。

850
heinob

JQueryでは、 シャローコピー / extend とすることができます。

var copiedObject = jQuery.extend({}, originalObject)

その後のcopyObjectへの変更は、originalObjectには影響しません。その逆も同様です。

または ディープコピーを作成するには

var copiedObject = jQuery.extend(true, {}, originalObject)
749
Pascal

ECMAScript 6には Object.assign methodがあり、列挙可能なすべての独自プロパティの値をあるオブジェクトから別のオブジェクトにコピーします。例えば:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

ただし、ネストしたオブジェクトはまだ参照としてコピーされていることに注意してください。

600

Per _ mdn _

  • シャローコピーが必要な場合は、Object.assign({}, a)を使用してください。
  • "ディープ"コピーにはJSON.parse(JSON.stringify(a))を使います

外部ライブラリは必要ありませんが、最初に ブラウザの互換性を確認する必要があります

175
Tareq

多くの答えがありますが、ECMAScript 5の Object.create について言及している人はいません。確かに正確なコピーは得られませんが、新しいオブジェクトのプロトタイプとしてソースが設定されます。

したがって、これは質問に対する正確な答えではありませんが、1行のソリューションであり、したがってエレガントです。そしてそれは2つのケースに最適です。

  1. そのような継承が便利なところ(当たり前)
  2. ソースオブジェクトは変更されないため、2つのオブジェクト間の関係は問題になりません。

例:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

このソリューションが優れていると考えるのはなぜですか?ネイティブなので、ループも再帰もありません。ただし、古いブラウザではポリフィルが必要になります。

128
itpastorn

1行のコードでJavascriptオブジェクトを複製するためのエレガントな方法

Object.assignメソッドはECMAScript 2015(ES6)標準の一部であり、まさにあなたが必要とすることを行います。

var clone = Object.assign({}, obj);

Object.assign()メソッドは、列挙可能なすべての独自プロパティの値を1つ以上のソースオブジェクトからターゲットオブジェクトにコピーするために使用されます。

続きを読む...

polyfill は古いブラウザをサポートします。

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;
    }
  });
}
115
Eugene Tiurin

浅いコピーで大丈夫なら、underscore.jsライブラリには clone メソッドがあります。

y = _.clone(x);

またはあなたはそれを拡張することができます

copiedObject = _.extend({},originalObject);
75
dule

インターネット上のほとんどのソリューションにはいくつかの問題があります。そこで、私はフォローアップをすることにしました。それには、受け入れられた答えが受け入れられない理由が含まれます。

開始状況

deep-copy そのすべての子とその子を含むJavascript Objectなどにします。しかし、私は普通の開発者ではないので、私のObjectにはnormalpropertiescircular structuresそしてnested objectsさえあります。

それでは、最初にcircular structurenested objectを作成しましょう。

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

すべてをObjectという名前のaにまとめましょう。

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

次に、abという名前の変数にコピーしてそれを変更します。

var b = a;

b.x = 'b';
b.nested.y = 'b';

あなたがここで何が起こったのか知っています。

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

それでは解決策を見つけましょう。

JSON

私が試した最初の試みはJSONの使用でした。

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

あまり時間を無駄にしないでください、あなたはTypeError: Converting circular structure to JSONを得るでしょう。

再帰的コピー(受け入れられた "答え")

受け入れられた答えを見てみましょう。

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

よさそうだね?これはオブジェクトの再帰的コピーであり、Dateのような他の型も処理しますが、それは必須条件ではありませんでした。

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

再帰とcircular structuresはうまく連携しません... RangeError: Maximum call stack size exceeded

ネイティブソリューション

私の同僚と議論した後、私の上司は何が起こったのか私達に尋ねました、そして彼はあるグーグルの後に簡単なsolutionを見つけました。それはObject.createと呼ばれます。

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

このソリューションはしばらく前にJavascriptに追加され、circular structureも処理します。

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

...そしてお分かりのように、内部の入れ子構造ではうまくいきませんでした。

ネイティブソリューション用のpolyfill

IE 8のように、古いブラウザにはObject.createのポリフィルがあります。もちろん、これは完全ではなく、ネイティブソリューションと同じ問題になります_。

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

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

私はFをスコープ外に置いたので、instanceofが教えてくれるものを見ることができます。

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

ネイティブソリューションと同じ問題ですが、出力が少し悪くなります。

より良い(しかし完璧ではない)解決策

調べてみると、これとよく似た質問( Javascriptでは、ディープコピーを実行するときに、プロパティが "this"であるためにどうすればサイクルを回避できますか? )が見つかりました。 。

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

そして出力を見てみましょう...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

要件は一致していますが、instancenestedcircObjectに変更するなど、まだいくつかの小さな問題があります。

葉を共有する木の構造はコピーされず、2つの独立した葉になります。

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

結論

再帰とキャッシュを使用する最後の解決策は最善ではないかもしれませんが、それはオブジェクトの real ディープコピーです。これは単純なpropertiescircular structures、およびnested objectを処理しますが、クローン作成中にそれらのインスタンスをめちゃくちゃにします。

jsfiddle

75
Fabio Poloni

OK、 あなたがこのオブジェクトを下に持っていて、それを複製したいと想像してください。

let obj = {a:1, b:2, c:3}; //ES6

または

var obj = {a:1, b:2, c:3}; //ES5

答えは主に ECMAscript を使っていることに依存しています、ES6+では、クローンをするために単にObject.assignを使うことができます:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

または、このようにスプレッド演算子を使用します。

let cloned = {...obj}; //new {a:1, b:2, c:3};

しかしES5を使うのであれば、いくつかのメソッドを使うことができますが、JSON.stringifyでは、大量のデータをコピーするのに使わないように気をつけてください。

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
43
Alireza

特に洗練されていない解決策の1つは、JSONエンコーディングを使用して、メンバーメソッドを持たないオブジェクトのディープコピーを作成することです。方法論は、ターゲットオブジェクトをJSONエンコードし、それをデコードすることで、探しているコピーを取得することです。必要なだけコピーを作成したいだけ何度でもデコードできます。

もちろん、関数はJSONに属していないため、これはメンバーメソッドを持たないオブジェクトに対してのみ機能します。

JSON BLOBをKey-Valueストアに格納し、それらがJavaScript APIのオブジェクトとして公開されている場合、各オブジェクトには実際にオブジェクトの元の状態のコピーが含まれているため、この方法は私のユースケースに最適です。呼び出し側が公開オブジェクトを変更した後でデルタを計算できます。

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value
38
Kris Walker

参照なしでオブジェクトをコピーするには、単純に spreadプロパティ を使用します。しかし、注意してください(コメント参照)、 'コピー'はちょうど最低のオブジェクト/配列レベルです。入れ子になったプロパティはまだ参照です。


完全なクローン:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

第2レベルの参照でクローンを作成します。

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScriptは実際にはディープクローンをネイティブにサポートしていません。効用関数を使用してください。例えばRamda:

http://ramdajs.com/docs/#clone

32
musemind

AngularJSを使用している人のために、このライブラリのオブジェクトを複製または拡張するための直接的な方法もあります。

var destination = angular.copy(source);

または

angular.copy(source, destination);

Angular.copyの詳細 ドキュメント ...

24
Lukas Jelinek

A.Levyの答えはほぼ完成しています、ここに私の小さな貢献があります: 再帰的参照を処理する方法があります 、この行を見てください

if(this[attr]==this) copy[attr] = copy;

オブジェクトがXML DOM要素の場合は、代わりに cloneNode を使用する必要があります。

if(this.cloneNode) return this.cloneNode(true);

A.Levyの徹底的な調査とCalvinのプロトタイピングアプローチに触発されて、私はこのソリューションを提供します:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

答えの中のAndy Burkeのメモも見てください。

22
Jan Turoň

ES-6では、単にObject.assign(...)を使用できます。例:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

参考になるには: https://googlechrome.github.io/samples/object-assign-es6/

19
João Oliveira

この記事から: Javascriptで配列やオブジェクトをコピーする方法 Brian Huismanによる:

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

これはあなたが使用できる関数です。

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}
19
picardo

ECMAScript 2018の場合

let objClone = { ...obj };

ネストしたオブジェクト はまだ参照としてコピーされている ことに注意してください。

17
Pavan Garre

1行のコードを使用して、オブジェクトのクローンを作成して前のオブジェクトから参照を削除することができます。簡単に

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

現在Object.createをサポートしていないブラウザ/エンジンの場合は、このpolyfillを使用できます。

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}
14
Rob Evans

古い質問に対する新しい答えです。 ECMAScript 2016(ES6)を Spread Syntax と共に使用したことがあるのであれば、それは簡単です。

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

これはオブジェクトの浅いコピーのためのクリーンな方法を提供します。ディープコピーを作成する、つまりすべての再帰的にネストされたオブジェクトのすべての値の新しいコピーを作成するには、上記のより重い解決策が必要です。

JavaScriptは進化し続けています。

13
Charles Merriam
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

ES6ソリューションで、プロパティオブジェクトだけでなくクラスインスタンスを(浅い)複製したい場合。

10
flori

Lodashを使う:

var y = _.clone(x, true);
10
VaZaA

単純なオブジェクトの複製に興味があります。

JSON.parse(JSON.stringify(json_original));

ソース: JavaScriptオブジェクトを参照ではなく新しい変数にコピーする方法

9
Mohammed Akdim

簡単で効果的な答えがあると思います。ディープコピーでは、2つの懸念があります。

  1. プロパティを互いに依存させます。
  2. そして、メソッドをクローンオブジェクト上で有効にします。

だから私は1つの簡単な解決策は最初に直列化し、逆シリアル化してから関数をコピーするためにそれに代入をすることになると思う。

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

この質問には多くの答えがありますが、これも役立つことを願っています。

8
ConductedClever

上記のJanTuroňの答えは非常に近いため、互換性の問題からブラウザで使用するのが最善の方法かもしれませんが、それは潜在的にいくつかの奇妙な列挙問題を引き起こすでしょう。例えば、

for ( var i in someArray ) { ... }

配列の要素を繰り返し処理した後、clone()メソッドをiに割り当てます。これは列挙を避けてnode.jsと連携する適応です。

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

これにより、clone()メソッドが列挙可能になるのを避けられます。defineProperty()のデフォルトはfalseに列挙可能であるためです。

6
Andy Burke
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;
};
6
user1547016

これは、関数のクローンと複数/巡回参照も処理するためのA. Levyのコードの修正です。つまり、クローンされるツリー内の2つのプロパティが同じオブジェクトの参照である場合、クローンオブジェクトツリーはこれらを持ちます。プロパティは、参照先オブジェクトのまったく同じクローンを指します。これはまた、処理されないままでいると無限ループにつながる循環依存の場合も解決します。アルゴリズムの複雑さはO(n)です。

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

簡単なテスト

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
6
Radu Simionescu

この記事のすべてのObject.createソリューションに追加したいのですが、これはnodejsでは望みどおりには機能しません。

Firefoxでは、

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

です

{test:"test"}

Nodejsではそれは

{}
6
heinob

私は自分自身の実装を書きました。それがより良い解決策として数えるかどうかわからない:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

実装は次のとおりです。

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].Push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}
6
yazjisuhail

mindeavour はクローンを作成するオブジェクトは 'リテラル構築'オブジェクトであると述べているので、解決策はオブジェクトのインスタンスを複製するのではなく単純に generate オブジェクトを複数回使用することです。

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
5
Bert Regelink

W3Cの「構造化データの安全な受け渡し」アルゴリズムについては、 http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data を参照してください。ウェブワーカーなどにデータを渡すためにブラウザによって実装されることを意図しています。ただし、機能を処理しないという点で、いくつかの制限があります。 JSの代わりのアルゴリズムを含め、詳細については https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm を参照してください。

4
user663031

Bellowは私のバージョンのディープクローニングで、機能をカバーし、循環参照を処理します。

https://github.com/radsimu/UaicNlpToolkit/blob/master/Modules/GGS/GGSEngine/src/main/resources/ro/uaic/info/nlptools/ggs/engine/core/jsInitCode.js#L17

4
Radu Simionescu

Npmからdeepcopyを使用してください。ブラウザとノードの両方でnpmモジュールとして動作します。

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

a = deepcopy(b)とします。

4
user3071643

構造化クローニング

HTML標準には、 内部の構造化クローン作成/直列化アルゴリズム が含まれており、これによってオブジェクトの深いクローンを作成できます。それはまだ特定の組み込み型に制限されていますが、JSONでサポートされているいくつかの型に加えて、日付、正規表現、マップ、セット、BLOB、ファイルリスト、ImageDatas、スパース配列、型付き配列などもサポートします。 。また、クローンデータ内の参照も保持されるため、JSONでエラーが発生する可能性がある循環的および再帰的な構造をサポートできます。

Node.jsでのサポート:実験的???

Node.jsのv8モジュールは現在(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つのポートを介してデータをポストすることです。もう一方のポートは、付属の.dataの構造化クローンとともにmessageイベントを発行します。残念ながら、これらのイベントをリスニングするのは必然的に非同期であり、同期の代替方法は実用的ではありません。

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();
3
Jeremy

「テンプレート」に基づいてオブジェクトを複製します。正確なコピーが欲しくないけれど、ある種の信頼できるクローン操作の堅牢性が欲しいのなら、ビットだけをクローンしたい、あるいは各属性値の存在やフォーマットを制御できるようにしたいのなら、どうしますか。クローン?

これは私たちにとって有用なものであり、類似したものが見つからなかったために作成したものです。これを使用して、複製したいオブジェクトの属性を指定する 'template'オブジェクトに基づいてオブジェクトを複製することができます。テンプレートでは、ソースオブジェクトに存在しない場合、関数がそれらの属性を異なるものに変換することができます。それとも、あなたはクローンを処理したいのです。それが役に立たない場合、私は誰かがこの答えを削除することができると確信しています。

   function isFunction(functionToCheck) {
       var getType = {};
       return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
   }

   function cloneObjectByTemplate(obj, tpl, cloneConstructor) {
       if (typeof cloneConstructor === "undefined") {
           cloneConstructor = false;
       }
       if (obj == null || typeof (obj) != 'object') return obj;

       //if we have an array, work through it's contents and apply the template to each item...
       if (Array.isArray(obj)) {
           var ret = [];
           for (var i = 0; i < obj.length; i++) {
               ret.Push(cloneObjectByTemplate(obj[i], tpl, cloneConstructor));
           }
           return ret;
       }

       //otherwise we have an object...
       //var temp:any = {}; // obj.constructor(); // we can't call obj.constructor because TypeScript defines this, so if we are dealing with a TypeScript object it might reset values.
       var temp = cloneConstructor ? new obj.constructor() : {};

       for (var key in tpl) {
           //if we are provided with a function to determine the value of this property, call it...
           if (isFunction(tpl[key])) {
               temp[key] = tpl[key](obj); //assign the result of the function call, passing in the value
           } else {
               //if our object has this property...
               if (obj[key] != undefined) {
                   if (Array.isArray(obj[key])) {
                       temp[key] = [];
                       for (var i = 0; i < obj[key].length; i++) {
                           temp[key].Push(cloneObjectByTemplate(obj[key][i], tpl[key], cloneConstructor));
                       }
                   } else {
                       temp[key] = cloneObjectByTemplate(obj[key], tpl[key], cloneConstructor);
                   }
               }
           }
       }

       return temp;
   }

これを呼び出す簡単な方法は、次のようになります。

var source = {
       a: "whatever",
       b: {
           x: "yeah",
           y: "haha"
       }
   };
   var template = {
       a: true, //we want to clone "a"
       b: {
           x: true //we want to clone "b.x" too
       }
   }; 
   var destination = cloneObjectByTemplate(source, template);

属性が返されることを確認するため、または特定の型であることを確認するために関数を使用したい場合は、次のようなテンプレートを使用してください。 {ID:true}を使用する代わりに、ソースオブジェクトのID属性をコピーするだけの関数を提供していますが、ソースオブジェクトに存在しなくても確実に数値になるようにします。

 var template = {
    ID: function (srcObj) {
        if(srcObj.ID == undefined){ return -1; }
        return parseInt(srcObj.ID.toString());
    }
}

配列はうまくクローンしますが、あなたが望むのであればあなた自身の関数にそれらの個々の属性も処理させることができ、そしてこのような特別なことをすることができます:

 var template = {
    tags: function (srcObj) {
        var tags = [];
        if (process.tags != undefined) {
            for (var i = 0; i < process.tags.length; i++) {

                tags.Push(cloneObjectByTemplate(
                  srcObj.tags[i],
                  { a : true, b : true } //another template for each item in the array
                );
            }
        }
        return tags;
    }
 }

そのため、上記では、テンプレートはソースオブジェクトの 'tags'属性が存在する場合はそれをコピーするだけで(配列と見なされます)、その配列の各要素に対してclone関数が呼び出されて、これらのタグ要素のそれぞれの 'a'属性と 'b'属性をコピーするだけの2番目のテンプレート。

オブジェクトをノードに出し入れする場合、それらのオブジェクトのどの属性を複製するかを制御したい場合、これはnode.jsでそれを制御する優れた方法であり、コードはブラウザでも機能します。

これがその使用例です: http://jsfiddle.net/hjchyLt1/

3
Action Dan

(以下は主に@ Maciej Bukowski 、@ A. Levy 、@ JanTuroň 、@ Redu の回答、および@ LeviRoberts の統合でした。 、@ RobG のコメント、ありがとうございました!!!)

ディープコピー ? - はい! (ほとんど)
シャローコピー ? - いや! (Proxyを除く).

私は心からclone()をテストすることを歓迎します。
さらに、defineProp()は、あらゆる種類の記述子を簡単かつ迅速にコピーするように設計されています。

関数

function clone(object) {
  /*
    Deep copy objects by value rather than by reference,
    exception: `Proxy`
  */

  const seen = new WeakMap()

  return (function clone(object) {
    if (object !== Object(object)) return object /*
    —— Check if the object belongs to a primitive data type */

    if (object instanceof Node) return object.cloneNode(true) /*
    —— Clone DOM trees */

    let _object // The clone of object

    switch (object.constructor) {
      case Object:
      case Array:
        _object = cloneObject(object)
        break

      case Date:
        _object = new Date(+object)
        break

      case Function:
        const fnStr = String(object)

        _object = new Function("return " +
          (/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
            ? "function " : ""
          ) + fnStr
        ).call(object)

        Object.defineProperties(_object,
          Object.getOwnPropertyDescriptors(object)
        )
        break

      default:
        switch (Object.prototype.toString.call(object.constructor)) {
          //                              // Stem from:
          case "[object Function]":       // `class`
          case "[object Undefined]":      // `Object.create(null)`
            _object = cloneObject(object)
            break

          default:                        // `Proxy`
            _object = object
        }
    }

    return _object


    function cloneObject(object) {
      if (seen.has(object)) return seen.get(object) /*
      —— Handle recursive references (circular structures) */

      const _object = Array.isArray(object)
        ? []
        : Object.create(Object.getPrototypeOf(object)) /*
          —— Assign [[Prototype]] for inheritance */

      seen.set(object, _object) /*
      —— Make `_object` the associative mirror of `object` */

      Reflect.ownKeys(object).forEach(key =>
        defineProp(_object, key, { value: clone(object[key]) }, object)
      )

      return _object
    }
  })(object)
}


function defineProp(object, key, descriptor = {}, copyFrom = {}) {
  const prevDesc = Object.getOwnPropertyDescriptor(object, key)
    || { configurable: true, writable: true }
    , copyDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
      || { configurable: true, writable: true } // Custom…
      || {} // …or left to native default settings

  const { configurable: _configurable, writable: _writable } = prevDesc
    , test = () => _writable === undefined
      ? _configurable // Can redefine property
      : _configurable && _writable // Can assign to property

  if (arguments.length <= 2) return test()
  if (!test()) return false;

  ["get", "set", "value", "writable", "enumerable", "configurable"]
    .forEach(k =>
      descriptor[k] === undefined && (descriptor[k] = copyDesc[k])
    )

  const { get, set, value, writable, enumerable, configurable }
    = descriptor

  return Object.defineProperty(object, key, get || set
    ? { get, set, enumerable, configurable } // Accessor descriptor
    : { value, writable, enumerable, configurable } // Data descriptor
  )
}

//テスト

"use strict"
const obj0 = {

  u: undefined,

  nul: null,

  t: true,

  n: 9,

  str1: "string",

  str2: "",

  sym: Symbol("symbol"),

  [Symbol("e")]: Math.E,

  f: {
    getAccessorStr(object) {
      return []
        .concat(...
          Object.values(Object.getOwnPropertyDescriptors(object))
            .filter(desc => desc.writable === undefined)
            .map(desc => Object.values(desc))
        )
        .filter(prop => typeof prop === "function")
        .map(String)
    },
    f0: function f0() { },
    f1: function () { },
    f2: a => a / (a + 1),
    f3: () => 0,
    f4(params) { return param => param + params },
    f5: (a, b) => ({ c = 0 } = {}) => a + b + c
  },

  o: {
    n: 0,
    o: {
      f: function (...args) { }
    }
  },

  arr: [[0], [1, 2]],

  d: new Date(),

  get g() { return 0 }
}

defineProp(obj0, "s", {
  set(v) { this._s = v }
})
defineProp(obj0.arr, "tint", {
  value: { is: "non-enumerable" }
})
obj0.arr[0].name = "nested array"


let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", {
  set(v) { this._s = v + 1 }
})

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Routinely")

console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()

console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()

console.log("obj0\n ",
  ".arr.tint:", obj0.arr.tint, "\n ",
  ".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
  ".arr.tint:", obj1.arr.tint, "\n ",
  ".arr[0].name:", obj1.arr[0].name
)
console.log()

console.log("Accessor-type descriptor\n ",
  "of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
  "of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
  "set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
  "  → (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)

console.log("—— obj0 has not been interfered.")

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Circular structures")

obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr

obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)

console.log("Clear obj0's recursion:",
  obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
  "obj0\n ",
  ".o.r:", obj0.o.r, "\n ",
  ".arr:", obj0.arr
)
console.log(
  "obj1\n ",
  ".o.r:", obj1.o.r, "\n ",
  ".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")


console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Classes")

class Person {
  constructor(name) {
    this.name = name
  }
}

class Boy extends Person { }
Boy.prototype.sex = "M"

const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }

const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"

boy0.name = "one"
boy1.name = "neo"

console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
  Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)

参考文献

  1. Object.create() | MDN
  2. Object.defineProperties() | MDN
  3. プロパティの列挙性と所有権| MDN
  4. TypeError:周期オブジェクト値| MDN
3
ooo

機能的クロージャを使用すると、ディープコピーなしで、ディープコピーのすべての利点を得ることができます。これはまったく異なるパラダイムですが、うまく機能します。既存のオブジェクトをコピーするのではなく、必要に応じて関数を使用して新しいオブジェクトをインスタンス化します。

まず、オブジェクトを返す関数を作成します

function template() {
  return {
    values: [1, 2, 3],
    nest: {x: {a: "a", b: "b"}, y: 100}
  };
}

それから簡単な浅いコピー機能を作成しなさい

function copy(a, b) {
  Object.keys(b).forEach(function(key) {
    a[key] = b[key];
  });
}

新しいオブジェクトを作成し、それにテンプレートのプロパティをコピーします。

var newObject = {}; 
copy(newObject, template());

しかし、上記のコピー手順は必要ありません。あなたがする必要があるのはこれだけです:

var newObject = template();

新しいオブジェクトを作成したので、次にそのプロパティが何であるかを確認します。

console.log(Object.keys(newObject));

これは表示します:

["values", "nest"]

はい、これらはnewObject自身のプロパティであり、他のオブジェクトのプロパティへの参照ではありません。チェックしてみましょう:

console.log(newObject.nest.x.b);

これは表示します:

"b"

NewObjectはテンプレートオブジェクトのすべてのプロパティを取得しましたが、依存関係はありません。

http://jsbin.com/ISUTIpoC/1/edit?js,console

私はいくつかの議論を促すためにこの例を追加したので、いくつかのコメントを追加してください:)

3
d13

Airbnb JavaScriptスタイルガイドによると404人の貢献者と:

オブジェクトを浅くコピーするオブジェクトにはObject.assignよりオブジェクト展開演算子を優先します。特定のプロパティを省略した新しいオブジェクトを取得するには、オブジェクトのrest演算子を使用します。

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

また、Airbnbはオブジェクトスプレッド演算子によるアプローチをほとんど推奨していませんが、警告します。 Microsoft Edgeはまだこの2018年の機能をまだサポートしていないことに注意してください。

ES2016 +コンパクトテーブル>>

3
Zsolt Gulyás

lodash _.cloneDeep()を使用

浅いコピー:lodash _.clone()

参照をコピーするだけで浅いコピーを作成できます。

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}

Shallow Copy: lodash _.clone()

ディープコピー:lodash _.cloneDeep()

フィールドは逆参照されます:コピーされるオブジェクトへの参照ではなく

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

Deep Copy: lodash _.cloneDeep()

2
Ashok R

キャッシングのある再帰 がライブラリなしでここでできることが一番良いと思います。

そして過小評価された WeakMap は、古いオブジェクトと新しいオブジェクトへの参照のペアを格納することで、ツリー全体を非常に簡単に再作成できるというサイクルの問題になります。

DOM要素のディープクローニングを防ぎました。おそらくページ全体をクローンしたくないでしょう:)

function deepCopy(object) {
    const cache = new WeakMap(); // Map of old - new references

    function copy(obj) {
        if (typeof obj !== 'object' ||
            obj === null ||
            obj instanceof HTMLElement
        )
            return obj; // primitive value or HTMLElement

        if (obj instanceof Date) 
            return new Date().setTime(obj.getTime());

        if (obj instanceof RegExp) 
            return new RegExp(obj.source, obj.flags);

        if (cache.has(obj)) 
            return cache.get(obj);

        const result = obj instanceof Array ? [] : {};

        cache.set(obj, result); // store reference to object before the recursive starts

        if (obj instanceof Array) {
            for(const o of obj) {
                 result.Push(copy(o));
            }
            return result;
        }

        const keys = Object.keys(obj); 

        for (const key of keys)
            result[key] = copy(obj[key]);

        return result;
    }

    return copy(object);
}

いくつかのテスト:

// #1
const obj1 = { };
const obj2 = { };
obj1.obj2 = obj2;
obj2.obj1 = obj1; // Trivial circular reference

var copy = deepCopy(obj1);
copy == obj1 // false
copy.obj2 === obj1.obj2 // false
copy.obj2.obj1.obj2 // and so on - no error (correctly cloned).

// #2
const obj = { x: 0 }
const clone = deepCopy({ a: obj, b: obj });
clone.a == clone.b // true

// #3
const arr = [];
arr[0] = arr; // A little bit weird but who cares
clone = deepCopy(arr)
clone == arr // false;
clone[0][0][0][0] == clone // true;

注:私は定数、forループ、=>演算子、およびWeakMapsを使用して、より重要なコードを作成しています。この構文(ES6)は今日のブラウザでサポートされています

2
Maciej Bukowski

私はスカラーオブジェクトの場合にこれを試しました、そして、それは私のために働きます:

function binder(i) {
  return function () {
    return i;
  };
}

a=1;
b=binder(a)(); // copy value of a into b

alert(++a); // 2
alert(b); // still 1

よろしく。

2
John Sonderson

ディープコピーのために私が使っているのは:

obj = { a: 0 , b: { c: 0}};
  let deepClone = JSON.parse(JSON.stringify(obj));
  obj.a = 5;
  obj.b.c = 5;
  console.log(JSON.stringify(obj)); // { a: 5, b: { c: 5}}
  console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
1
Nishant Dwivedi

defaults (歴史的にはnodejsに固有のものですが、現在は現代のJSのおかげでブラウザから使用可能です):

import defaults from 'object.defaults';

const myCopy = defaults({}, myObject);
1
aberaud

オブジェクトに循環的な依存関係がない場合は、他の答えの1つまたは jQueryのコピーメソッド を使用することをお勧めします。これらはすべて非常に効果的です。

循環的な依存関係がある場合(つまり、2つのサブオブジェクトが相互にリンクしている場合)、(理論的観点から) この問題をエレガントに解決する方法はありません のようにねじ込まれます。

1
Daniel Lew

わかりましたので、これは浅いコピーのための非常に最善の選択肢かもしれません。 ifを使って多くの例に従うならば、それは継承とプロトタイプも保ちます。これもとても単純で、コンストラクタの要件や読み取り専用のプロパティを持つものを除いて、ほとんどの配列に似たオブジェクトやオブジェクトに対して機能します。しかしそれは、TypedArrays、RegExp、Date、Maps、Sets、およびObjectバージョンのプリミティブ(Boolean、Stringなど)では惨めに失敗することを意味します。

function copy ( a ) { return Object.assign( new a.constructor, a ) }

aは任意のObjectまたはクラスで構築されたインスタンスになることができますが、特殊なゲッターやセッターを使用するもの、あるいはコンストラクター要件を持つものには信頼できませんが、もっと単純な状況では揺るぎないです。それは議論にも働きます。

あなたは奇妙な結果を得るためにプリミティブにそれを適用することもできます、しかしそれから...それが単に気にかけている有用なハックであることにならない限り。

基本的な組み込みオブジェクトと配列の結果.

> a = { a: 'A', b: 'B', c: 'C', d: 'D' }
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> b = copy( a )
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> a = [1,2,3,4]
[ 1, 2, 3, 4 ]
> b = copy( a )
[ 1, 2, 3, 4 ]

そして、get/settersという意味で、コンストラクタが引数や読み取り専用のプロパティを必要とし、そして父親に対して罪を犯したために失敗しました。

> a = /\w+/g
/\w+/g
> b = copy( a )  // fails because source and flags are read-only
/(?:)/
> a = new Date ( '1/1/2001' )
2000-12-31T16:00:00.000Z
> b = copy( a )  // fails because Date using methods to get and set things
2017-02-04T14:44:13.990Z
> a = new Boolean( true )
[Boolean: true]
> b = copy( a )  // fails because of of sins against the father
[Boolean: false]
> a = new Number( 37 )
[Number: 37]
> b = copy( a )  // fails because of of sins against the father
[Number: 0]
> a = new String( 'four score and seven years ago our four fathers' )
[String: 'four score and seven years ago our four fathers']
> b = copy( a )  // fails because of of sins against the father
{ [String: ''] '0': 'f', '1': 'o', '2': 'u', '3': 'r', '4': ' ', '5': 's', '6': 'c', '7': 'o', '8': 'r', '9': 'e', '10': ' ', '11': 'a', '12': 'n', '13': 'd', '14': ' ', '15': 's', '16': 'e', '17': 'v', '18': 'e', '19': 'n', '20': ' ', '21': 'y', '22': 'e', '23': 'a', '24': 'r', '25': 's', '26': ' ', '27': 'a', '28': 'g', '29': 'o', '30': ' ', '31': 'o', '32': 'u', '33': 'r', '34': ' ', '35': 'f', '36': 'o', '37': 'u', '38': 'r', '39': ' ', '40': 'f', '41': 'a', '42': 't', '43': 'h', '44': 'e', '45': 'r', '46': 's' } 
1
Erich Horn

これはObject.assign()の落とし穴を持たない(参照によってコピーしない)現代的な解決策です:

const cloneObj = (obj) => {
    return Object.keys(obj).reduce((dolly, key) => {
        dolly[key] = (obj[key].constructor === Object) ?
            cloneObj(obj[key]) :
            obj[key];
        return dolly;
    }, {});
};
1
ryanpcmcquen

オブジェクトのコピーに関する理解を深めるために、 この例示的なjsbinは価値があるかもしれません

class base {
  get under(){return true}
}

class a extends base {}

const b = {
  get b1(){return true},
  b: true
}

console.log('Object assign')
let t1 = Object.create(b)
t1.x = true
const c = Object.assign(t1, new a())
console.log(c.b1 ? 'prop value copied': 'prop value gone')
console.log(c.x ? 'assigned value copied': 'assigned value gone')
console.log(c.under ? 'inheritance ok': 'inheritance gone')
console.log(c.b1 ? 'get value unchanged' : 'get value lost')
c.b1 = false
console.log(c.b1? 'get unchanged' : 'get lost')
console.log('-----------------------------------')
console.log('Object assign  - order swopped')
t1 = Object.create(b)
t1.x = true
const d = Object.assign(new a(), t1)
console.log(d.b1 ? 'prop value copied': 'prop value gone')
console.log(d.x ? 'assigned value copied': 'assigned value gone')
console.log(d.under ? 'inheritance n/a': 'inheritance gone')
console.log(d.b1 ? 'get value copied' : 'get value lost')
d.b1 = false
console.log(d.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e = { ...t1, ...t2 }
console.log(e.b1 ? 'prop value copied': 'prop value gone')
console.log(e.x ? 'assigned value copied': 'assigned value gone')
console.log(e.under ? 'inheritance ok': 'inheritance gone')
console.log(e.b1 ? 'get value copied' : 'get value lost')
e.b1 = false
console.log(e.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator on getPrototypeOf')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e1 = { ...Object.getPrototypeOf(t1), ...Object.getPrototypeOf(t2) }
console.log(e1.b1 ? 'prop value copied': 'prop value gone')
console.log(e1.x ? 'assigned value copied': 'assigned value gone')
console.log(e1.under ? 'inheritance ok': 'inheritance gone')
console.log(e1.b1 ? 'get value copied' : 'get value lost')
e1.b1 = false
console.log(e1.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('keys, defineProperty, getOwnPropertyDescriptor')
f = Object.create(b)
t2 = new a()
f.x = 'a'
Object.keys(t2).forEach(key=> {
  Object.defineProperty(f,key,Object.getOwnPropertyDescriptor(t2, key))
})
console.log(f.b1 ? 'prop value copied': 'prop value gone')
console.log(f.x ? 'assigned value copied': 'assigned value gone')
console.log(f.under ? 'inheritance ok': 'inheritance gone')
console.log(f.b1 ? 'get value copied' : 'get value lost')
f.b1 = false
console.log(f.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('defineProperties, getOwnPropertyDescriptors')
let g = Object.create(b)
t2 = new a()
g.x = 'a'
Object.defineProperties(g,Object.getOwnPropertyDescriptors(t2))
console.log(g.b1 ? 'prop value copied': 'prop value gone')
console.log(g.x ? 'assigned value copied': 'assigned value gone')
console.log(g.under ? 'inheritance ok': 'inheritance gone')
console.log(g.b1 ? 'get value copied' : 'get value lost')
g.b1 = false
console.log(g.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
1
TrevTheDev

オブジェクトをコピーすることに関する問題は、最終的にはそれ自体を指す可能性がありますが、簡単なチェックで解決できます。コピー操作があるたびに、このチェックを追加してください。それは slow かもしれませんが、should _ workです。

オブジェクト型を明示的に返すために toType() 関数を使用します。私は自分自身の copyObj() 関数も持っています。これは論理的にはかなり似ていて、3つのObject()、Array()、Date()のすべてのケースに答えます。

NodeJSで実行しています。

未テスト、まだ

// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
  if (toType(parent) == '[object Object]') {
    for (var name in parent) {
      var curProperty = parent[name];

      // Direct child.
      if (curProperty = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
        if (isChild(target, curProperty)) return true;
      }
    }
  } else if (toType(parent) == '[object Array]') {
    for (var i=0; i < parent.length; i++) {
      var curItem = parent[i];

      // Direct child.
      if (curItem = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
        if (isChild(target, curItem)) return true;
      }
    }
  }

  return false;     // Not the target.
}
0
James Koss

どのケースでこれがうまくいかないかはわかりませんが、配列のコピーを取得しました。私はそのかわいいと思う:)それが助けることを願って

copiedArr = origArr.filter(function(x){return true})
0
knowingpark

JSON.stringifyが扱うことができない円形のオブジェクトを扱うために、 _ jsog _ と呼ばれるライブラリを持ち込むことができます。

var clone = JSOG.parse(JSOG.stringify(original));

このトリックを使ってクローン作成のためにJSOGにパッチを当てるのも面白いかもしれません(現時点では時間がありませんが、誰かが試してみたい場合は...)。

単純な関数を直列化します。

foo.f = function(a) { return a }
var stringForm = foo.f.toString() // "function (a) { return a }"

関数を逆シリアル化します。

eval("foo.f = " + stringForm)

通常の文字列に対して関数を識別するためのいくつかの規約(おそらくプロパティの名前で)が必要になります(おそらく@func_f)。

もちろん、関数が2番目の関数を呼び出す場合、2番目の関数は元の関数と同じように存在する必要があります。

信頼できないソースから直列化された形式を受け入れるのであれば上記は非常に危険ですが、信頼できないソースからどんな形式の関数でも受け入れることは危険ですので、クローン関数に興味があるなら信頼はすでに確立されているはずです(あるいはすでにセキュリティ上の欠陥を書くつもりです!).

免責事項: 私はJSOGのstringify/parseとJSONのstringify/parseの速度をテストしていませんが、テストした単純な(円形の)オブジェクトでは動作します。

0
Gus

あなたがJSONfnでそれをすることができるあなたが関数を持っているオブジェクトを得たならば、 http://www.eslinstructor.net/jsonfn/ を見なさい。

var obj= {
    name:'Marvin',
    getName :  function(){
      return this.name;
    }
}
var cobj = JSONfn.parse(JSONfn.stringify(obj));
0
basis

親オブジェクトを変更せずに複製することができます -

    /** [Object Extend]*/
    ( typeof Object.extend === 'function' ? undefined : ( Object.extend = function ( destination, source ) {
        for ( var property in source )
            destination[property] = source[property];
        return destination;
    } ) );
    /** [/Object Extend]*/
    /** [Object clone]*/
    ( typeof Object.clone === 'function' ? undefined : ( Object.clone = function ( object ) {
        return this.extend( {}, object );
    } ) );
    /** [/Object clone]*/

    let myObj = {
        a:1, b:2, c:3, d:{
            a:1, b:2, c:3
        }
    };

    let clone = Object.clone( myObj );

    clone.a = 10;

    console.log('clone.a==>', clone.a); //==> 10

    console.log('myObj.a==>', myObj.a); //==> 1 // object not modified here

    let clone2 = Object.clone( clone );

    clone2.a = 20;

    console.log('clone2.a==>', clone2.a); //==> 20

    console.log('clone.a==>', clone.a); //==> 10 // object not modified here
0
RKTUXYN

私は同じ質問のためにこのページに来ましたが、私はJQueryを使用していませんし、clone-Methodsも私のオブジェクトに対しては機能しませんでした。

私の答えはこの質問にあまり強く関連していないことを私は知っています。それは異なるアプローチだからです。クローン関数を使う代わりに、私はcreate関数を使います。それは私にとって次のような(残念ながら制限する)目的のために働きました:

  1. 私は主にJSPで生成されたJavascriptを使っています
  2. 私は最初にどのObjectを生成しなければならないかを知っています(私の場合は一度取得されてJSからもっと頻繁にデプロイされる必要があるのはDatabaseからの情報です).

最初に私はこのように私のオブジェクトを定義しました:

var obj= new Object();
obj.Type='Row';
obj.ID=1;
obj.Value='Blah blah';

今、私はすべてのように動かしました:

function getObjSelektor(id_nummer,selected){
var obj = document.createElement("select");
obj.setAttribute("id","Selektor_"+id_nummer);
obj.setAttribute("name","Selektor");
obj.setAttribute("size","1");

var obj_opt_1 = document.createElement("option");
obj_opt_1.setAttribute("value","1");
if(1==selected)
    posopval_opt_1.setAttribute("selected","selected");
obj_opt_1.innerHTML="Blah blah";
obj.appendChild(obj_opt_1);

var obj_opt_2 = document.createElement("option");
obj_opt_2.setAttribute("value","2");
if(2==selected)
    obj_opt_2.setAttribute("selected","selected");
obj_opt_2.innerHTML="2nd Row";
obj.appendChild(obj_opt_2);

...

return obj;
}

そして通常のコードで関数を呼び出します。

myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));

すでに述べたように、これは私の目的のために私の問題を解決した別のアプローチです。

0
Qohelet
//
// creates 'clone' method on context object
//
//  var 
//     clon = Object.clone( anyValue );
//
!((function (propertyName, definition) {
    this[propertyName] = definition();
}).call(
    Object,
    "clone",
    function () {
        function isfn(fn) {
            return typeof fn === "function";
        }

        function isobj(o) {
            return o === Object(o);
        }

        function isarray(o) {
            return Object.prototype.toString.call(o) === "[object Array]";
        }

        function fnclon(fn) {
            return function () {
                fn.apply(this, arguments);
            };
        }

        function owns(obj, p) {
            return obj.hasOwnProperty(p);
        }

        function isemptyobj(obj) {
            for (var p in obj) {
                return false;
            }
            return true;
        }

        function isObject(o) {
            return Object.prototype.toString.call(o) === "[object Object]";
        }
        return function (input) {
            if (isfn(input)) {
                return fnclon(input);
            } else if (isobj(input)) {
                var cloned = {};
                for (var p in input) {
                    owns(Object.prototype, p)
                    || (
                        isfn(input[p])
                        && ( cloned[p] = function () { return input[p].apply(input, arguments); } )
                        || ( cloned[p] = input[p] )
                    );
                }
                if (isarray(input)) {
                    cloned.length = input.length;
                    "concat every filter forEach indexOf join lastIndexOf map pop Push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift"
                    .split(" ")
                    .forEach(
                      function (methodName) {
                        isfn( Array.prototype[methodName] )
                        && (
                            cloned[methodName] =
                            function () {
                                return Array.prototype[methodName].apply(cloned, arguments);
                            }
                        );
                      }
                    );
                }
                return isemptyobj(cloned)
                       ? (
                          isObject(input)
                          ? cloned
                          : input
                        )
                       : cloned;
            } else {
                return input;
            }
        };
    }
));
//
0
public override

Object.assign()メソッドは、列挙可能なすべての独自プロパティの値を1つ以上のソースオブジェクトからターゲットオブジェクトにコピーするために使用されます。ターゲットオブジェクトを返します。

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }

構文

Object.assign(target, ...sources)

Object.assign()メソッドは、列挙可能な独自のプロパティをソースオブジェクトからターゲットオブジェクトにコピーするだけです。ソースに[[Get]]を、ターゲットに[[Set]]を使います。

そのため、ゲッターとセッターが呼び出されます。したがって、単に新しいプロパティをコピーまたは定義するのではなく、プロパティを割り当てます。マージソースにゲッターが含まれている場合、これは新しいプロパティをプロトタイプにマージするのに不適切な場合があります。列挙型を含むプロパティ定義をプロトタイプにコピーするには、代わりにObject.getOwnPropertyDescriptor()Object.defineProperty()を使用してください。

StringプロパティとSymbolプロパティの両方がコピーされます。

たとえば、エラーが発生した場合、プロパティが書き込み不可の場合はTypeErrorが発生し、エラーが発生する前にプロパティが追加されている場合はターゲットオブジェクトを変更できます。

Object.assign()nullまたはundefinedのソース値をスローしません。

0
ashish

シンプル

var restore = { name:'charlesi',
age:9}
var prev_data ={
name: 'charles'
age : 10
}

var temp = JSON.stringify(prev_data)
restore = JSON.parse(temp)

restore = {
name:'charlie',
age : 12}

出力prev_data:

{
name: 'charles'
age : 10
} 
0

Apple JavaScriptコーディングガイドラインから

// Create an inner object with a variable x whose default
// value is 3.
function innerObj()
{
        this.x = 3;
}
innerObj.prototype.clone = function() {
    var temp = new innerObj();
    for (myvar in this) {
        // this object does not contain any objects, so
        // use the lightweight copy code.
        temp[myvar] = this[myvar];
    }
    return temp;
}

// Create an outer object with a variable y whose default
// value is 77.
function outerObj()
{
        // The outer object contains an inner object.  Allocate it here.
        this.inner = new innerObj();
        this.y = 77;
}
outerObj.prototype.clone = function() {
    var temp = new outerObj();
    for (myvar in this) {
        if (this[myvar].clone) {
            // This variable contains an object with a
            // clone operator.  Call it to create a copy.
            temp[myvar] = this[myvar].clone();
        } else {
            // This variable contains a scalar value,
            // a string value, or an object with no
            // clone function.  Assign it directly.
            temp[myvar] = this[myvar];
        }
    }
    return temp;
}

// Allocate an outer object and assign non-default values to variables in
// both the outer and inner objects.
outer = new outerObj;
outer.inner.x = 4;
outer.y = 16;

// Clone the outer object (which, in turn, clones the inner object).
newouter = outer.clone();

// Verify that both values were copied.
alert('inner x is '+newouter.inner.x); // prints 4
alert('y is '+newouter.y); // prints 16

スティーブ

0
Steve Harrison

このリンク が次のコードを使用すると言うように:
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

0
Pouria Moosavi

あなたのオブジェクトがクラスの場合(例: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes ):

var copiedObject = jQuery.extend(true, {}, originalObject);
copiedObject.__proto__ = originalObject.__proto__;

それからcopiedObjectにあなたはそのすべてのメソッドを持つoriginalObjectクラスのディープコピーされたインスタンスがあります。

0
j rdl

TypeScriptを使用していて、古いWebブラウザをサポートする必要があり(したがってObject.assignを使用できない)、クローンメソッドが組み込まれたライブラリを使用していない場合は、数行のコードでcombineヘルパーを使用できます。それはオブジェクトを結合し、あなたが一つしか持っていなければ、ただそれを複製します。

/** Creates a new object that combines the properties of the specified objects. */
function combine(...objs: {}[]) {
    const combined = {};
    objs.forEach(o => Object.keys(o).forEach(p => combined[p] = o[p]));
    return combined;
}
0
Edward Brey

私はこの質問に対する答えを提供しています。なぜなら、ここではDOM要素の問題を解決するためのネイティブで再帰的な実装を見ていないからです。

問題は<element>parentchild属性を持ち、それらがparentchildの値を持つ他の要素にリンクしていて、それが元の<element>を指していることで、 無限再帰 または 巡回冗長 を引き起こします。

あなたのオブジェクトが安全でシンプルなものであれば

{
    '123':456
}

...それなら、ここで他の答えはおそらくうまくいくでしょう。

しかし、あなたが持っているなら...

{
    '123':<reactJSComponent>,
    '456':document.createElement('div'),
}

...それなら、次のようなものが必要です。

    // cloneVariable() : Clone variable, return null for elements or components.
var cloneVariable = function (args) {
    const variable = args.variable;

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

    if(typeof(variable) === 'object') {
            if(variable instanceof HTMLElement || variable.nodeType > 0) {
                    return null;
            }

            if(Array.isArray(variable)) {
                    var arrayclone = [];

                    variable.forEach((element) => {
                            arrayclone.Push(cloneVariable({'variable':element}));
                    });

                    return arrayclone;
            }

            var objectclone = {};

            Object.keys(variable).forEach((field) => {
                    objectclone[field] = cloneVariable({'variable':variable[field]});
            });

            return objectclone;
    }

    return variable;
}
0
HoldOffHunger

私のコードでは、コピーを処理するための関数(_)を頻繁に定義しているので、関数に "値で"渡すことができます。このコードはディープコピーを作成しますが継承を維持します。また、自己参照オブジェクトを無限ループなしでコピーできるように、サブコピーを追跡します。お気軽にご利用ください。

それは最もエレガントではないかもしれませんが、それはまだ私を失敗させていません。

_ = function(oReferance) {
  var aReferances = new Array();
  var getPrototypeOf = function(oObject) {
    if(typeof(Object.getPrototypeOf)!=="undefined") return Object.getPrototypeOf(oObject);
    var oTest = new Object();
    if(typeof(oObject.__proto__)!=="undefined"&&typeof(oTest.__proto__)!=="undefined"&&oTest.__proto__===Object.prototype) return oObject.__proto__;
    if(typeof(oObject.constructor)!=="undefined"&&typeof(oTest.constructor)!=="undefined"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!=="undefined") return oObject.constructor.prototype;
    return Object.prototype;
  };
  var recursiveCopy = function(oSource) {
    if(typeof(oSource)!=="object") return oSource;
    if(oSource===null) return null;
    for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];
    var Copy = new Function();
    Copy.prototype = getPrototypeOf(oSource);
    var oCopy = new Copy();
    aReferances.Push([oSource,oCopy]);
    for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);
    return oCopy;
  };
  return recursiveCopy(oReferance);
};

// Examples:
Wigit = function(){};
Wigit.prototype.bInThePrototype = true;
A = new Wigit();
A.nCoolNumber = 7;
B = _(A);
B.nCoolNumber = 8; // A.nCoolNumber is still 7
B.bInThePrototype // true
B instanceof Wigit // true
0
Alec