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 つの解決策 (要約)

推薦する

Docker コンテナのログを表示およびクリーンアップする方法 (テスト済みで効果的)

1. 問題Docker コンテナのログにより、ホストのディスク領域がいっぱいになりました。 doc...

mysql ERROR 1045 (28000) 問題の解決方法

私はmysql ERROR 1045に遭遇し、この問題に長い時間を費やしました。私はそれを自分で書き...

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

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

MySQL (InnoDB) がデッドロックを処理する方法の詳細な説明

1. デッドロックとは何ですか?正式な定義は次のとおりです: 2 つのトランザクションが相手側で必要...

JavaScript の 3 つの BOM オブジェクト

目次1. 場所オブジェクト1. URL 2. 場所オブジェクトのプロパティ3. ロケーションオブジェ...

MySQL アクティブ-アクティブ同期レプリケーションの 4 つのソリューションの詳細な説明

目次MySQLネイティブレプリケーションに基づくマスター-マスター同期ソリューションGaleraレプ...

Angular環境構築と簡単な体験のまとめ

Angular入門Angular は、Google が開発したオープンソースの Web フロントエン...

iFrameは背景を覆うポップアップレイヤーとして使うのに最適です

最近、私は「ぶどうコレクション」というプロジェクトに取り組んでいます。簡単に言うと、Budou ペー...

MySQLのエンコードの不一致によって発生する可能性のある問題

ストアドプロシージャとコーディングMySQL ストアド プロシージャでは、テーブルとデータのエンコー...

Vue3 の組み合わせ API における setup、ref、reactive の完全な使用方法

1. セットアップを始める次のコード関数を簡単に紹介します。 ref 関数を使用して変数の変更を監視...

Nginx プロセス管理とリロードの原則の詳細な説明

プロセス構造図Nginx はマルチプロセス構造です。マルチプロセス構造は、次のような Nginx の...

Linux ディスクのマウント、パーティション分割、容量拡張操作を実装する方法

基本概念操作の前に、まずいくつかの基本的な概念を理解する必要がありますディスクLinux システムで...

Vue 監視属性のグラフィック例の詳細な説明

目次リスナープロパティとは何ですか?リスニングプロパティと計算プロパティの違いは何ですか?監視プロパ...

Bash スクリプトでの配列メソッドの作成と使用の概要

Bashで配列を定義するbash スクリプトで新しい配列を作成する方法は 2 つあります。 1 つ目...

Dell R720 サーバーに Windows Server 2008 R2 をインストールする方法

注: この記事のすべての写真はインターネットから収集されたものであるため、DELL R720 サーバ...