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

推薦する

MySQL データの集約とグループ化

多くの場合、データを実際に取得せずに要約する必要があり、 MySQLこの目的のために特別な関数を提供...

Centos7.4 環境に lamp-php7.0 をインストールするチュートリアル

この記事では、Centos7.4 環境に lamp-php7.0 をインストールする方法について説明...

ポートマッピング後に Docker コンテナが突然接続に失敗する問題のトラブルシューティング プロセス

1. 背景通常、外部サービスを提供する必要がある Docker コンテナの場合、起動時に -p コマ...

JavaScriptのスタックとコピーの詳細な説明

目次1. スタックの定義2. JSスタックの調査1. スタックとヒープ2. 基本型と参照型3. 値渡...

MySQLインスタンスが起動できない問題の分析と解決

目次序文シナリオ分析要約する序文数日前、友人がWeChatで私に連絡してきて、マシンがダウンタイムか...

ウェブデザイン実践者必読のキャリアプラン

原文記事、転載の際は著者と出典を明記してください! Web デザインは間違いなくテクノロジーであり、...

Windows 10 で MySql の解凍バージョンをインストールして構成する方法のチュートリアル

Windows 10 で MySql データベースの解凍バージョンをインストールするステップ 1: ...

擬似静的およびクライアント適応型 Nginx の設定方法

バックエンドは thinkphp3.2.3 フレームワークを使用します。他の言語を使用している場合は...

Linux環境でIPV6接続をサポートするようにmysql5.6を設定する方法

導入:この記事では主に、Linux システムで IPV6 接続をサポートするように MySQL を構...

Dockerはコンテナに入るためにルートを使用する

まずdockerコンテナを実行しますルートユーザーとしてコマンドを実行する sudo docker ...

CSSはcalc()を使用して現在の表示画面の高さを取得します

まず、CSS3 の相対的な長さの単位を見てみましょう (詳細なチュートリアルを参照してください)。相...

レスポンシブ Web デザインが価値のない 5 つの理由

この記事は Tom Ewer の Managewp ブログからのもので、現在人気のレスポンシブ デザ...

Vueのドラッグスクリーンショット機能を実装する簡単な方法

マウスをドラッグしてページのスクリーンショットを撮ります(指定した領域にスクリーンショットをドラッグ...

Vue シングルファイルコンポーネントの実装

最近、vue について読みました。これまで基本的に見落としていた単一ファイル コンポーネントを見つけ...

動的なテーブル効果を実現するJavaScript

この記事では、動的なテーブル効果を実現するためのJavaScriptの具体的なコードを参考までに紹介...