javascriptES6クラスインスタンスのクローンを作成する方法


96

ES6を使用してJavascriptクラスインスタンスのクローンを作成するにはどうすればよいですか。

jqueryまたは$ extendに基づくソリューションには興味がありません。

問題が非常に複雑であることを示唆するオブジェクトのクローン作成に関するかなり古い議論を見てきましたが、ES6では非常に単純な解決策が提示されます-以下にそれを置き、人々がそれで十分だと思うかどうかを確認します。

編集:私の質問は重複していることが示唆されています。私はその答えを見ましたが、それは7歳であり、ES6以前のjsを使用した非常に複雑な答えを含んでいます。ES6を可能にする私の質問には、劇的に単純な解決策があることを示唆しています。


2
Stack Overflowの古い質問に対する新しい回答がある場合は、新しい質問を作成するだけでなく、元の質問にその回答を追加してください。
異端の猿

1
ES6クラスインスタンスは「通常の」オブジェクトとは異なる動作をするため、トムが直面している/直面していた問題がわかります。
CherryNerd 2017年

2
また、受け入れられた回答の最初のコードは、ES6クラスのインスタンスで実行しようとすると実際にクラッシュします
CherryNerd 2017年

ES6クラスインスタンスはオブジェクトですが、すべてのオブジェクトがES6クラスインスタンスであるとは限らないため、これは重複ではないと思います。したがって、他の質問ではこの質問の問題に対処していません。
トマシュザト-モニカを復活させる2017年

5
重複ではありません。もう1つの質問はObject、データホルダーとして使用される純粋なものについてでした。これはES6esclassとクラスタイプ情報を失わないための問題に関するものです。別の解決策が必要です。
flori 2017年

回答:


111

それは複雑です; たくさんやってみました!結局、このワンライナーは私のカスタムES6クラスインスタンスで機能しました。

let clone = Object.assign(Object.create(Object.getPrototypeOf(orig)), orig)

コードの速度が大幅に低下すると言われているため、プロトタイプの設定を回避します。

シンボルをサポートしますが、ゲッター/セッターには最適ではなく、列挙できないプロパティでは機能しません(Object.assign()のドキュメントを参照)。また、基本的な内部クラス(Array、Date、RegExp、Mapなど)のクローンを作成するには、悲しいことに、個別の処理が必要になることがよくあります。

結論:それは混乱です。いつの日か、ネイティブでクリーンなクローン機能が登場することを期待しましょう。


1
静的メソッドは実際には列挙可能な独自のプロパティではないため、これは静的メソッドをコピーしません。
ラバランプ氏2017

5
@ Mr.Lavalampと、静的メソッドを(また)どのようにコピーできますか?
flori 2017

これはアレイを破壊します!すべての配列を「0」、「1」、...キーを持つオブジェクトに変換します。
Vahid 2018年

1
@KeshaAntonovtypeofメソッドとArrayメソッドを使用して解決策を見つけることができる場合があります。私自身、すべてのプロパティを手動で複製することを好みました。
Vahid 2018年

1
それ自体がオブジェクトであるプロパティのクローンを作成することを期待しないでください:jsbin.com/qeziwetexu/edit
js、

10
const clone = Object.assign( {}, instanceOfBlah );
Object.setPrototypeOf( clone, Blah.prototype );

Object.assignの特性に注意してください浅いコピーを実行し、クラスメソッドをコピーしません。

ディープコピーまたはコピーのより詳細な制御が必要な場合は、lodashクローン関数があります


2
Object.create指定されたプロトタイプで新しいオブジェクトを作成するので、それではなぜですかconst clone = Object.assign(Object.create(instanceOfBlah), instanceOfBlah)。また、クラスメソッドもコピーされます。
barbatus 2017

1
@barbatusしかし、それは間違ったプロトタイプを使用していますBlah.prototype != instanceOfBlah。使用する必要がありますObject.getPrototypeOf(instanceOfBlah)
ベルギ2017

1
@Bergiいいえ、ES6クラスインスタンスには常にプロトタイプがあるとは限りません。codepen.io/techniq/pen/qdZeZmをチェックして、インスタンスでも機能することを確認してください。
barbatus 2017

@barbatusすみません、何ですか?私はフォローしません。すべてのインスタンスにはプロトタイプがあり、それがインスタンスになります。floriの答えからコードを試してください。
ベルギ2017

1
@BergiBabelの設定か何かによると思います。私は現在、リアクティブなネイティブアプリを実装していますが、継承されたプロパティのないインスタンスにはプロトタイプnullがあります。あなたがここに見ることができるようにまたdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/... それはgetPrototypeOfがnullを返すことが可能です。
barbatus 2017

3

プロトタイプの拡張を行うことはお勧めしません。コード/コンポーネントでテストを行うときに問題が発生します。単体テストフレームワークは、プロトタイプの拡張を自動的に想定しません。したがって、これは良い習慣ではありません。プロトタイプ拡張の説明はここにありますネイティブオブジェクトを拡張するのはなぜ悪い習慣なのですか?

JavaScriptでオブジェクトのクローンを作成するには、単純で簡単な方法はありません。これが「ShallowCopy」を使用した最初のインスタンスです。

1->浅いクローン:

class Employee {
    constructor(first, last, street) {
        this.firstName = first;
        this.lastName = last;
        this.address = { street: street };
    }

    logFullName() {
        console.log(this.firstName + ' ' + this.lastName);
    }
}

let original = new Employee('Cassio', 'Seffrin', 'Street A, 23');
let clone =  Object.assign({},original); //object.assing() method
let cloneWithPrototype Object.create(Object.getPrototypeOf(original)), original) //  the clone will inherit the prototype methods of the original.
let clone2 = { ...original }; // the same of object assign but shorter sintax using "spread operator"
clone.firstName = 'John';
clone.address.street = 'Street B, 99'; //will not be cloned

結果:

original.logFullName():

結果:Cassio Seffrin

clone.logFullName():

結果:ジョン・セフリン

original.address.street;

結果: 'ストリートB、99' //元のサブオブジェクトが変更されたことに注意してください

注意:インスタンスに独自のプロパティとしてクロージャがある場合、このメソッドはそれをラップしません。(クロージャの詳細を読む)さらに、サブオブジェクト「アドレス」は複製されません。

clone.logFullName()

動作しないでしょう。

cloneWithPrototype.logFullName()

クローンはそのプロトタイプもコピーするため、機能します。

Object.assignを使用して配列のクローンを作成するには:

let cloneArr = array.map((a) => Object.assign({}, a));

ECMAScriptスプレッドsintaxを使用したクローン配列:

let cloneArrSpread = array.map((a) => ({ ...a }));

2->ディープクローン:

完全に新しいオブジェクト参照をアーカイブするには、JSON.stringify()を使用して元のオブジェクトを文字列として解析し、解析後にJSON.parse()に戻します。

let deepClone = JSON.parse(JSON.stringify(original));

ディープクローンを使用すると、アドレスへの参照が保持されます。ただし、deepCloneプロトタイプは失われるため、deepClone.logFullName()は機能しません。

3->サードパーティライブラリ:

別のオプションは、loadashやアンダースコアなどのサードパーティライブラリを使用することです。新しいオブジェクトを作成し、各値を元のオブジェクトから新しいオブジェクトにコピーして、その参照をメモリに保持します。

アンダースコア:cloneUnderscore = _(original).clone();

Loadashクローン:var cloneLodash = _.cloneDeep(original);

lodashまたはアンダースコアの欠点は、プロジェクトにいくつかの追加ライブラリを含める必要があることでした。ただし、これらは優れたオプションであり、高性能の結果も生成します。


1
に割り当てる場合{}、クローンは元のプロトタイプメソッドを継承しません。clone.logFullName()まったく機能しません。Object.assign( Object.create(Object.getPrototypeOf(eOriginal)), eOriginal)あなたが前に持っていたが、なぜあなたはそれを変更でしたが、大丈夫でしたか?
ベルギ

1
@Bergiはあなたの貢献に感謝します、私は今私の答えを編集していました、私はプロトタイプをコピーするためにあなたのポイントを追加しました!
CassioSeffrin19年

1
@Bergiのご協力に感謝します。今すぐご意見をお聞かせください。エディションを終了しました。私は今、答えはほとんどすべての質問をカバーしていると思います。Thks!
CassioSeffrin19年

1
はい、そして同じようにObject.assign({},original)、それは機能しません。
ベルギ

1
必要なのは、より単純なアプローチだけの場合もあります。あなたが必要としない場合はプロトタイプおよび複雑なオブジェクトは単に「クローン= {...}オリジナルの」問題を解決できること
カシオSeffrin

0

もう1つのライナー:

ほとんどの場合...(Date、RegExp、Map、String、Number、Arrayで機能します)、ところで、文字列のクローン作成、numberは少しおかしいです。

let clone = new obj.constructor(...[obj].flat())

コピーコンストラクタのないクラスの場合:

let clone = Object.assign(new obj.constructor(...[obj].flat()), obj)

0
class A {
  constructor() {
    this.x = 1;
  }

  y() {
    return 1;
  }
}

const a = new A();

const output = Object.getOwnPropertyNames(Object.getPrototypeOf(a)).concat(Object.getOwnPropertyNames(a)).reduce((accumulator, currentValue, currentIndex, array) => {
  accumulator[currentValue] = a[currentValue];
  return accumulator;
}, {});

ここに画像の説明を入力してください


-4

たとえば、Objという名前のオブジェクトのクローンを作成する場合は、spread演算子を使用できます。

let clone = { ...obj};

また、複製されたオブジェクトに何かを変更または追加する場合は、次のようにします。

let clone = { ...obj, change: "something" };
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.