JavaScriptの浅いコピーと深いコピーについての簡単な説明

JavaScriptの浅いコピーと深いコピーについての簡単な説明

このトピックについては、インターネット上で多くの議論が行われています。私はさまざまな状況に応じて自分でそれらを整理しました。最終的には、ディープ コピーをほぼ完璧に実現することができました。皆さんの議論をお待ちしています。

javascriptのオブジェクトは参照型です。オブジェクトをコピーするときは、浅いコピーを使用するか深いコピーを使用するかを検討する必要があります。

1. 直接譲渡

オブジェクトは参照型です。別のオブジェクトに直接割り当てられている場合は、単なる参照です。実際、2 つの変数は同じデータ オブジェクトを指します。1 つのオブジェクトのプロパティが変更されると、もう 1 つのオブジェクトのプロパティも変更されます。

例1、簡単な例:

人間1 = {
    id: 1,
    名前: 「ハッピー」
};
human2 = human1; // ここでは直接代入です console.log(human1); // {id: 1, name: 'happy'}
console.log(human2); // {id: 1, name: 'happy'}
 
// human1 の名前を変更すると human2 の名前も変更されます。human1.name = "life";
console.log(human1); // {id: 1, 名前: 'life'}
console.log(human2); // {id: 1, 名前: 'life'}

例 2: オブジェクトをパラメータとして渡すと参照も渡されます。

人間1 = {
    id: 1,
    名前: 「ハッピー」
};
 
console.log(human1); // {id: 1, name: 'happy'}
 
関数 foo(人間) {
    // ここで人間オブジェクトの名前が変更されます human.name = "life";
}
foo(human1); // オブジェクトは参照渡しです console.log(human1); // {id: 1, name: 'life'}

2. 浅いコピー

浅いコピーでは、オブジェクトの最初のレイヤーのみがコピーされます。最初のレイヤーのプロパティ値がオブジェクトの場合、プロパティへの参照のみがコピーされます。

オブジェクト1 = {
    a: 1、
    b: { // bはオブジェクト b1: 2
    }
};
object2 = Object.assign({}, object1); // これは浅いコピーであり、オブジェクト b の参照のみがコピーされます // a は通常の型であり、互いに影響しません object1.a = 10;
コンソール.log(オブジェクト1.a); // 10
コンソール.log(object2.a); // 1
 
// b は相互に影響を与えるオブジェクトです。object1.b.b1 = 20;
コンソール.log(object1.b.b1); // 20
コンソール.log(object2.b.b1); // 20


完全なコピーを実現するには、ディープ コピーを使用する必要があります。

3. ディープコピー

Sen コピーとは、1 つのレイヤーをコピーするだけでなく、その内部のレイヤー (オブジェクトの場合) もコピーする必要があることを意味します。

1. JSONオブジェクトメソッド

オブジェクトがJSONオブジェクトであることが確認できる場合は、 JSONオブジェクト形式で使用できます。

上記の例を使用します。

オブジェクト1 = {
    a: 1、
    b: { // bはオブジェクト b1: 2
    }
};
 
object2 = JSON.parse(JSON.stringify(object1)); // ディープコピー // a は通常の型なので互いに影響しません。object1.a = 10;
コンソール.log(オブジェクト1.a); // 10
コンソール.log(object2.a); // 1
 
// b はオブジェクトなので互いに影響しません object1.b.b1 = 20;
コンソール.log(object1.b.b1); // 20
コンソール.log(object2.b.b1); // 2


ここでのディープ コピーの原理は、実際にはオブジェクトを最初にjson文字列に変換し、次にjsonオブジェクトに変換することです。JSON json列に変換された後は、元のオブジェクトとは何の関係もありません。

この方法の利点:実装が非常に簡単です。

欠点:

属性値が関数である場合、コピーできず、データが失われます。
また、プロトタイプ オブジェクトはコピーできません。

したがって、このメソッドは純粋なjsonデータであることが確認されたオブジェクトにのみ適しています。

2. 再帰コピー

レイヤーごとにコピーする必要があるため、再帰的なアプローチを使用することは簡単です。次の実装を参照してください。

関数 deepCopy(ソース) {
    // オブジェクトまたは null でない場合は直接返します if (typeof source !== 'object' || source === null) {
        ソースを返します。
    }
 
    ターゲットを{}とします。
    // プロパティをトラバースしてコピーする for (let k in source) {
        if (!source.hasOwnProperty(k)) {
            続く;
        }
 
        if (typeof source[k] === 'object') { // オブジェクトの場合は再帰的にコピーします target[k] = deepCopy(source[k]);
            続く;
        }
 
        記述子を Object.getOwnPropertyDescriptor(source, k); とします。
        Object.defineProperty(ターゲット、k、記述子);
    }
 
    ターゲットを返します。
}

オブジェクトはレイヤーごとにコピーされるため、コピーが完了した後、2 つのオブジェクトは互いに影響を与えず、メソッドもサポートされます。

オブジェクト1 = {
    a: 1、
    b: { // bはオブジェクト b1: 2
    },
    f: function() { // f はメソッドです console.log(3);
    }
};
object2 = deepCopy(object1); // ディープコピー。関数もコピーできます。
オブジェクト1.f(); // 3
オブジェクト2.f(); // 3
 
// b はオブジェクトなので互いに影響しません object1.b.b1 = 20;
コンソール.log(object1.b.b1); // 20
コンソール.log(object2.b.b1); // 2


プロトタイプオブジェクトのコピー

しかし、この方法にはまだ問題があります。つまり、プロトタイプ オブジェクトをコピーできないのです。これを少し改善してみましょう。

// プロトタイプもコピーされるように、let target = {}; を次のように変更します。let target = Object.create(Object.getPrototypeOf(source));


以上です。例を使って確認してみましょう。

関数Human() {
    id = 1;
}
Human.prototype.bar = 関数() {
    console.log("バー");
};
 
human1 を新しい Human() にします。
人間2 = deepCopy(人間1);
 
console.log("人間1", 人間1);
console.log("human2", human2);


次の 2 つのオブジェクトのプロトタイプを見てみましょう。

プロトタイプ オブジェクトをディープ コピーします。

完璧なコピーです。

もちろん、この方法には問題があります。再帰レベルが深すぎると、スタックオーバーフローが発生しやすくなります。ただし、実際には、非常に大きなオブジェクトをコピーしないことも推奨されます。他の適切な解決策があるはずです。

JavaScriptシャローコピーとディープコピーに関するこの記事はこれで終わりです。JavaScript JavaScriptシャローコピーとディープコピーに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

参考資料:

JS はディープコピーを実装します: https://www.cnblogs.com/dobeco/p/11295316.html
Object.assign(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.create(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Object.getPrototypeOf(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
Object.defineProperty(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Object.getOwnPropertyDescriptor(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
hasOwnProperty(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

以下もご興味があるかもしれません:
  • JavaScript のディープコピーとシャローコピーの詳しい説明
  • js における浅いコピーと深いコピーの詳細な説明
  • JS変数ストレージのディープコピーとシャローコピーの詳しい説明
  • JS オブジェクトのコピー (ディープ コピーとシャロー コピー)
  • JavaScriptのディープコピーとシャローコピーに関するよくある話

<<:  MySQL infobrightのインストール手順

>>:  クロスブラウザの問題に対する 5 つの解決策 (要約)

推薦する

JavaScriptのポイントごとのシリーズでこれは何ですか

これを理解するおそらく、他のオブジェクト指向プログラミング言語でもthis見たことがあり、これがコン...

LinuxでLVMディスクを拡張する詳細な手順

1.ハードディスクを追加する2. パーティションの状態を確認します: fdisk -l 3. パーテ...

MySQLデータベースで外部キー制約を使用する必要があるかどうかの詳細な説明

1. はじめに外部キー制約を使用するかどうかという話題は、すでに決まり文句になっています。学校では、...

...

scss で mixin が動作しない問題の解決方法 (ブラウザでコンパイルできない)

ミックスインメソッド: ブラウザはコンパイルできません: 以前のバージョンのsassでは上記の記述方...

イントラネット侵入を実現するためのSSHポート転送

LAN 内のマシンは外部ネットワークにアクセスできますが、外部ネットワークは内部ネットワークにアクセ...

MySQLはgroup_concat()関数に基づいて複数のデータ行を結合します

非常に便利な機能group_concat() について、マニュアルには次のように記載されています: ...

シンプルなカルーセルの最も完全なコード分析を実装するJavaScript(ES6オブジェクト指向)

この記事では、シンプルなカルーセルを実装するためのJavaScriptの具体的なコードを参考までに紹...

Linux サーバー上で nvidia-docker 環境を設定するプロセスの詳細な説明

Docker はコンテナに相当し、必要な動作環境に応じて対応する動作環境を構築できます。このとき、各...

過去2年間のユーザーエクスペリエンス

<br />国内のウェブサイトが本格的に普及し、ユーザーエクスペリエンスに重点が置かれる...

画像を表示したり非表示にしたりするための JavaScript

JavaScriptは画像を表示したり非表示にしたりしますが、参考までに具体的な内容は次のとおりで...

Tomcat の文字化けしたコードとポート占有の解決方法について簡単に説明します

Tomcat サーバーは、無料でオープン ソースの Web アプリケーション サーバーです。軽量のア...

URLパラメータに基づくNginx転送

使用シナリオ:ジャンプ パスは、傍受された URL に応じて動的に構成する必要があります。これは、イ...

win10にUbuntu18デュアルシステムをインストールするとmmx64.efiが見つからないという問題が発生する

Ubuntu 18のインストール中に、USBディスクからUbuntuのインストールを開始すると、mm...

2つのNode.jsプロセスがどのように通信するかの詳細な説明

目次序文異なるコンピュータ上の 2 つの Node.js プロセス間の通信TCPソケットの使用HTT...