3466

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

JavaScript オブジェクトを正しく複製するにはどうすればよいですか?

4

77 に答える 77

1689

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より 5 秒遅れd2ます。Date同じものを別のものと同じにする方法は、メソッドを呼び出すことですが、それはクラスsetTimeに固有です。Dateこの問題に対する防弾の一般的な解決策はないと思いますが、間違っていれば幸いです!

一般的なディープ コピーを実装しなければならなかったとき、単純なObject, Array, Date, String,NumberまたはBoolean. 最後の 3 つのタイプは不変であるため、浅いコピーを実行でき、変更を心配する必要はありません。Objectさらに、 orに含まれる要素はすべて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 つの単純な型に対して適切に機能します。つまり、オブジェクト内の同じデータへの参照は 1 つしかありません。例えば:

// 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オブジェクトを処理することはできませんが、投げたものすべてに対して機能すると想定しない限り、多くの目的には十分かもしれません.

于 2009-04-08T05:58:23.557 に答える
1104

Dateオブジェクト内で s、functions、undefined、regExp、または 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'
}
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()

これは、オブジェクト、配列、文​​字列、ブール値、および数値を含むすべての種類のオブジェクトで機能します。

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

于 2012-06-03T09:36:59.400 に答える
805

jQuery を使用すると、 extendを使用して浅いコピーを行うことができます。

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

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

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

var copiedObject = jQuery.extend(true, {}, originalObject)
于 2011-03-02T06:36:13.403 に答える
791

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

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

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

于 2015-05-05T01:46:28.397 に答える
303

MDNあたり:

  • 浅いコピーが必要な場合は、使用しますObject.assign({}, a)
  • 「ディープ」コピーの場合は、JSON.parse(JSON.stringify(a))

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

于 2016-11-07T17:14:38.383 に答える
143

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

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

  1. そのような継承が役立つ場合 (duh!)
  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

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

于 2012-03-19T15:17:52.737 に答える
139

1 行のコードで Javascript オブジェクトのクローンを作成するエレガントな方法

Object.assignメソッドは ECMAScript 2015 (ES6) 標準の一部であり、必要なことを正確に実行します。

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

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

続きを読む...

古いブラウザーをサポートするポリフィル:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}
于 2015-12-15T16:42:26.017 に答える
94

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

開始状況

Javascriptをそのすべての子とその子などとともにディープ コピーしたいと考えています。Objectしかし、私は普通の開発者ではないのでObject通常 propertiescircular structuresnested objects.

circular structureそれでは、最初に aと aを作成しましょうnested 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')
};

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

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

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

同僚と口論になった後、上司は何が起こったのか私たちに尋ねました。彼はグーグルで検索した後、簡単な解決策を見つけました。と呼ばれてい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"
    }
}

...そしてご覧のとおり、内部のネストされた構造では機能しませんでした。

ネイティブ ソリューションのポリフィル

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

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

instance要件は一致していますが、のnestedとのcircを に変更するなど、まだいくつかの小さな問題がありますObject

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

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

結論

再帰とキャッシュを使用する最後のソリューションは、最善ではないかもしれませんが、オブジェクトの実際のディープ コピーです。シンプルなpropertiescircular structuresおよびを処理nested objectしますが、クローン作成中にそれらのインスタンスを台無しにします。

jsfiddle

于 2014-07-09T08:37:55.783 に答える
80

浅いコピーでよければ、underscore.js ライブラリにcloneメソッドがあります。

y = _.clone(x);

または、次のように拡張できます

copiedObject = _.extend({},originalObject);
于 2012-06-13T23:44:10.383 に答える
47

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

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

この方法論は私のユースケースに最適でした。JSONBLOBを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
于 2009-10-29T20:24:06.643 に答える
30

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

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;
};
于 2009-04-08T03:13:46.963 に答える
30

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

var destination = angular.copy(source);

また

angular.copy(source, destination);

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

于 2014-09-03T19:08:28.020 に答える
27
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;
}
于 2009-04-08T03:16:14.087 に答える
26

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 のメモも参照してください。

于 2012-01-26T18:12:50.343 に答える
23

Lodash の使用:

var y = _.clone(x, true);
于 2012-12-13T00:05:07.187 に答える
17

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 Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}
于 2012-05-26T13:45:40.157 に答える
15

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

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

これにより、オブジェクトの浅いコピーのクリーンな方法が提供されます。再帰的にネストされたすべてのオブジェクトのすべての値の新しいコピーを作成することを意味する深いコピーを作成するには、上記のより重いソリューションが必要です。

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

于 2016-12-16T11:34:33.233 に答える
14

ディープ コピーとクローンの場合、オブジェクトを JSON.stringify してから JSON.parse します。

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
于 2019-05-20T08:35:11.347 に答える
11

(以下は主に @ Maciej Bukowski、@ A. Levy、@ Jan Turoň、@ 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 clone(object)


  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 Array:
      case Object:
        _object = cloneObject(object)
        break

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

      case Function:
        _object = copyFn(object)
        break

      case RegExp:
        _object = new RegExp(object)
        break

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

              case "AsyncFunction":
              case "GeneratorFunction":
              case "AsyncGeneratorFunction":
                _object = copyFn(object)
                break

              default:
                _object = object
            }
            break

          case "[object Undefined]":          // `Object.create(null)`
            _object = cloneObject(object)
            break

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

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


function copyPropDescs(target, source) {
  Object.defineProperties(target,
    Object.getOwnPropertyDescriptors(source)
  )
}


function convertFnToStr(fn) {
  let fnStr = String(fn)
  if (fn.name.startsWith("[")) // isSymbolKey
    fnStr = fnStr.replace(/\[Symbol\..+?\]/, '')
  fnStr = /^(?!(async )?(function\b|[^{]+?=>))[^(]+?\(/.test(fnStr)
    ? fnStr.replace(/^(async )?(\*)?/, "$1function$2 ") : fnStr
  return fnStr
}

function copyFn(fn) {
  const newFn = new Function(`return ${convertFnToStr(fn)}`)()
  copyPropDescs(newFn, fn)
  return newFn
}



function defineProp(object, key, descriptor = {}, copyFrom = {}) {
  const { configurable: _configurable, writable: _writable }
    = Object.getOwnPropertyDescriptor(object, key)
    || { configurable: true, writable: true }

  const test = _configurable // Can redefine property
    && (_writable === undefined || _writable) // Can assign to property

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

  const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
    || { configurable: true, writable: true } // Custom…
    || {}; // …or left to native default settings

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

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

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

// テスト

const obj0 = {
  u: undefined,
  nul: null,
  t: true,
  num: 9,
  str: "",
  sym: Symbol("symbol"),
  [Symbol("e")]: Math.E,
  arr: [[0], [1, 2]],
  d: new Date(),
  re: /f/g,
  get g() { return 0 },
  o: {
    n: 0,
    o: { f: function (...args) { } }
  },
  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
  }
}

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 } })
defineProp(obj1.re, "multiline", { value: true })

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 - More kinds of functions")

const fnsForTest = {
  f(_) { return _ },
  func: _ => _,
  aFunc: async _ => _,
  async function() { },
  async asyncFunc() { },
  aFn: async function () { },
  *gen() { },
  async *asyncGen() { },
  aG1: async function* () { },
  aG2: async function* gen() { },
  *[Symbol.iterator]() { yield* Object.keys(this) }
}

console.log(Reflect.ownKeys(fnsForTest).map(k =>
  `${String(k)}:
  ${fnsForTest[k].name}-->
    ${String(fnsForTest[k])}`
).join("\n"))

const normedFnsStr = `{
  f: function f(_) { return _ },
  func: _ => _,
  aFunc: async _ => _,
  function: async function() { },
  asyncFunc: async function asyncFunc() { },
  aFn: async function () { },
  gen: function* gen() { },
  asyncGen: async function* asyncGen() { },
  aG1: async function* () { },
  aG2: async function* gen() { },
  [Symbol.iterator]: function* () { yield* Object.keys(this) }
}`

const copiedFnsForTest = clone(fnsForTest)
console.log("fnsForTest:", fnsForTest)
console.log("fnsForTest (copied):", copiedFnsForTest)
console.log("fnsForTest (normed str):", eval(`(${normedFnsStr})`))
console.log("Comparison of fnsForTest and its clone:",
  Reflect.ownKeys(fnsForTest).map(k =>
    [k, fnsForTest[k] === copiedFnsForTest[k]]
  )
)

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

使用される言語トリック

  1. 条件付きで小道具をオブジェクトに追加する
于 2018-12-12T06:48:20.657 に答える
8

これは、関数の複製と複数/循環参照も処理するためのA. Levyのコードの適応です。つまり、複製されるツリー内の2つのプロパティが同じオブジェクトの参照である場合、複製されるオブジェクトツリーにはこれらが含まれます。プロパティは、参照されるオブジェクトの1つの同じクローンを指します。これにより、循環依存の場合も解決されます。この依存関係は、未処理のままにすると、無限ループになります。アルゴリズムの複雑さは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));
于 2012-07-12T23:15:38.623 に答える
6

mindeavorは、複製されるオブジェクトは「リテラルで構築された」オブジェクトであると述べているため、解決策は、オブジェクトのインスタンスを複製するのではなく、単にオブジェクトを複数回生成することです。

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

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
于 2012-11-28T18:48:47.300 に答える
6

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

Firefoxでは、

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

{test:"test"}

nodejsでは

{}
于 2012-06-03T09:29:51.493 に答える
6

私は自分の実装を書きました。それがより良い解決策と見なされるかどうかはわかりません:

/*
    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;
}
于 2014-11-12T01:25:16.797 に答える
6
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:43:48.160 に答える
5

から使用deepcopynpmます。ブラウザnodenpm module...

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

let a = deepcopy(b)

于 2016-08-05T18:49:37.657 に答える
5

オブジェクトを複製する単純な再帰的方法。lodash.clone も使用できます。

let clone = (obj) => {
	let obj2 = Array.isArray(obj) ? [] : {};
	for(let k in obj) {
          obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) :  obj[k];
        }
        return obj2;
    }

let w = { name: "Apple", types: ["Fuji", "Gala"]};
let x = clone(w);
w.name = "Orange";
w.types = ["Navel"];
console.log(x);
console.log(w);

于 2020-01-01T03:48:52.177 に答える
4

に基づいてオブジェクトを複製しますtemplate。正確なコピーは必要ないが、ある種の信頼できるクローン操作の堅牢性が必要であるが、ビットのクローンのみが必要な場合、または各属性値の存在または形式を確実に制御できるようにしたい場合はどうしますか?クローン?

これは私たちにとって有用であり、同様のものが見つからなかったため作成したため、これを提供しています。これを使用して、複製したいオブジェクトの属性を指定するオブジェクトに基づいてオブジェクトを複製でき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 attributeますが、ソース オブジェクトに存在しない場合でも、それが数値であることを確認します。

 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 関数が呼び出され、2 番目のテンプレートに基づいて個別に複製されます。これらの各タグ要素の属性aと属性をコピーするだけです。b

ノードの内外でオブジェクトを取得していて、それらのオブジェクトのどのアトリビュートを複製するかを制御したい場合、これはそれを制御する優れた方法でnode.jsあり、コードはブラウザでも機能します。

使用例を次に示します: http://jsfiddle.net/hjchyLt1/

于 2015-01-30T21:16:21.583 に答える
3

ファンクショナル クロージャーを使用すると、ディープ コピーを使用せずに、ディープ コピーのすべての利点を得ることができます。これは非常に異なるパラダイムですが、うまく機能します。既存のオブジェクトをコピーしようとする代わりに、必要なときに関数を使用して新しいオブジェクトをインスタンス化します。

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

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、コンソール

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

于 2014-01-16T21:52:49.180 に答える
3

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

最も洗練されたものではないかもしれませんが、まだ失敗したことはありません。

_ = 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
于 2013-01-07T18:30:05.627 に答える
1

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

循環的な依存関係 (つまり、2 つのサブオブジェクトが相互にリンクしている) がある場合、(理論的な観点から)この問題をエレガントに解決する方法がないため、ちょっと困ったことになります。

于 2009-04-08T03:16:47.957 に答える
1

これにより、 (参照だけでなく)新しいコピーobjが作成されます。

let myCopy = JSON.parse(JSON.stringify(obj)); 

..その後、はるかに効率的に動作し_.cloneDeep(obj)ます。

于 2021-08-27T08:27:49.360 に答える
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

スティーブ

于 2009-04-08T03:55:34.320 に答える
0
//
// 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;
            }
        };
    }
));
//
于 2013-07-27T15:53:28.723 に答える
0

JSON.stringify処理できない循環オブジェクトを処理するには、任意のグラフを JSON 形式にシリアル化および逆シリアル化する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 の速度はテストしていませんが、テストした単純な (循環) オブジェクトでは動作します。

于 2016-07-12T00:51:11.493 に答える
0

関数を持つオブジェクトを取得した場合は、JSONfn で実行できます。 http://www.eslinstructor.net/jsonfn/を参照してください。

var obj= {
    name:'Marvin',
    getName :  function(){
      return this.name;
    }
}
var cobj = JSONfn.parse(JSONfn.stringify(obj));
于 2013-09-25T06:48:13.793 に答える
0

最終的にそれ自体を指す可能性のあるオブジェクトをコピーする際の問題は、簡単なチェックで解決できます。コピー アクションがあるたびに、このチェックを追加します。遅いかもしれませんが、動作するはずです。

toType()関数を使用して、明示的にオブジェクト タイプを返します。また、独自のcopyObj()関数もありますが、これはロジックがかなり似ており、Object()、Array()、および Date() の 3 つのケースすべてに応答します。

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.
}
于 2014-02-09T10:16:57.013 に答える