JavaScript の深いコピーと浅いコピーにおける循環参照の問題を解決する方法

JavaScript の深いコピーと浅いコピーの紹介

JavaScript のディープ コピーとシャロー コピーは、オブジェクトと配列をコピーする2 つの異なる方法です。

浅いコピーとは、新しいオブジェクトまたは配列を作成することを指します。新しいオブジェクト/配列は、元のオブジェクト/配列の参照値をコピーするだけで、その内部のサブオブジェクト/配列はコピーしませんつまり、浅拷贝只复制了对象/数组的第一层,而不会递归地复制嵌套的对象/数组

ディープ コピーとは、新しいオブジェクトまたは配列を作成することを指します。新しいオブジェクト/配列は、内部のサブオブジェクト/配列を含む元のオブジェクト/配列のすべての値をコピーし、ネストされたすべてのオブジェクト/配列を再帰的にコピーします。

浅いコピーを実装する方法

浅いコピーとは、新しいオブジェクトまたは配列を作成することを指します。新しいオブジェクト/配列は、元のオブジェクト/配列の参照値をコピーするだけで、その内部のサブオブジェクト/配列はコピーしません。

浅いコピーを実装する一般的な方法をいくつか示します。

  1. スプレッド演算子: 浅いコピーは、スプレッド演算子 (…) を使用して実現できます。
const originalObj = {
    
     a: 1, b: 2 };
const shallowCopy = {
    
     ...originalObj };
  1. Object.assign() メソッド: Object.assign() メソッドは、元のオブジェクトのプロパティを新しいオブジェクトにコピーして、浅いコピーを実現します。
const originalObj = {
    
     a: 1, b: 2 };
const shallowCopy = Object.assign({
    
    }, originalObj);
  1. Array.prototype.slice() メソッド: 配列の場合、slice() メソッドを使用して元の配列の内容をコピーし、浅いコピーを実現できます。
const originalArr = [1, 2, 3];
const shallowCopy = originalArr.slice();
  1. Array.prototype.concat() メソッド: concat() メソッドを使用して、元の配列の内容を新しい配列に接続し、浅いコピーを実現します。
const originalArr = [1, 2, 3];
const shallowCopy = [].concat(originalArr);
  1. Array.from() メソッド: Array.from() メソッドを使用して、配列のようなオブジェクトまたは反復可能なオブジェクトを配列に変換し、浅いコピーを実装します。
const originalArr = [1, 2, 3];
const shallowCopy = Array.from(originalArr);

これらのメソッドは、元のオブジェクトまたは配列の浅いコピーを作成できます。元のオブジェクト/配列に参照タイプのサブオブジェクト/配列が含まれている場合、浅いコピーは参照値のみをコピーし、浅いコピーされたオブジェクト/配列を変更すると元のオブジェクト/配列にも影響することに注意してください。サブオブジェクト/配列をコピーする必要がある場合は、ディープ コピーの使用を検討してください。

ディープコピーの実装方法

ディープコピーとは、元のオブジェクトと同じ値を持つがメモリ内で完全に独立した新しいオブジェクトを作成することを意味します。ディープ コピーを実装するには、いくつかの方法があります。

  1. 手動コピー: 単純なオブジェクトの場合、これはパスです手动复制每个属性来实现深拷贝しかし、このアプローチは、特にオブジェクトが深くネストされている場合、退屈でエラーが発生しやすくなる可能性があります。

  2. JSON のシリアル化と逆シリアル化: を使用するとJSON 序列化和反序列化便利なディープ コピー方法です。オブジェクトを JSON 文字列に変換し、その文字列をオブジェクトに戻すことで、元のオブジェクトの正確なコピーが作成されます。

  3. シリアル化および逆シリアル化ライブラリ: 多くのプログラミング言語は序列化和反序列化、Python の pickle モジュール、Java の Serializable インターフェイスなどの特殊なライブラリを提供します。これらのライブラリは、元のオブジェクトの完全な構造を保持しながら、オブジェクト間でディープ コピーを作成できます。

  4. リフレクションと再帰の使用: 使用反射可以遍历对象的所有属性,并为每个属性创建一个完全独立的副本オブジェクトのプロパティがまだ複雑なオブジェクトである場合は、再帰を使用してサブオブジェクトのディープ コピーを実装できます。

どの方法を選択する場合でも、特定のプログラミング言語とシナリオに基づいて最適な実装を決定する必要がありますディープ コピーは、元のオブジェクトのデータ整合性を保護し、コピーが変更されたときに元のオブジェクトが影響を受けないようにする重要なテクノロジです。

ディープコピーを導入する際に注意すべき点は何ですか?

ディープ コピーを実装する場合、注意する必要がある問題がいくつかあります。

  1. 循環参照: ディープ コピーの間にオブジェクトがある場合、循环引用(例如对象A引用对象B,对象B又引用对象A)ディープ コピーは無限ループに陥る可能性があります。ディープ コピーを実装する場合は、循環参照の状況に対処し、サイクルを断ち切るための適切な措置を講じる必要があります。

  2. プロトタイプ チェーン: オブジェクトのプロトタイプ プロパティには、正確にディープ コピーすることが難しい関数やその他の変更可能なオブジェクトが含まれる場合があります。ディープ コピーを実装する場合、コピーの整合性を確保するために、プロトタイプ チェーン内のプロパティを慎重に処理する必要があります。

  3. シンボル プロパティと列挙不可能なプロパティ: オブジェクトには、シンボル タイプのプロパティまたは列挙不可能なプロパティが含まれる場合がありますが、これらはディープ コピー プロセス中に無視される場合があります。完全なディープ コピーを実現するには、シンボル プロパティと列挙不可能なプロパティも正しくコピーされていることを確認する必要があります。

  4. パフォーマンスの問題: ディープ コピーには再帰や大量のオブジェクトのコピーが含まれる場合があり、特に複雑なオブジェクトやネスト レベルが深いオブジェクトの場合、パフォーマンスの問題が発生する可能性があります。実際の使用では、ディープコピーの必要性とパフォーマンスのオーバーヘッドの間で適切なバランスを検討する必要があります。

これらの問題は通常、実際にディープ コピーを実装し、コピーされたオブジェクトが予期せぬ副作用なく完全であることを確認し、エッジ ケースを処理するときに注意する必要があります。

循環参照の問題を解決する方法

循環参照はディープ コピーでよくある問題であり、次の方法で解決できます。

  1. ハッシュ テーブルを使用してコピーされたオブジェクトを追跡する: ディープ コピー プロセス中に、コピーされたオブジェクトと対応するコピーを保存するためにハッシュ テーブルが維持されます。オブジェクトをコピーする場合は、まずハッシュテーブルにオブジェクトのコピーが存在するかどうかを確認し、存在する場合には再帰コピーを行わずにコピーの参照を直接返します。

  2. 変数を使用してコピーされたオブジェクトを追跡する: ディープ コピー プロセス中に、変数を使用してコピーされたオブジェクトを追跡できます。オブジェクトをコピーする場合は、オブジェクトを変数に格納し、他の属性を再帰的にコピーするときに判断し、コピーされたオブジェクトに遭遇した場合は、以前にコピーされた参照がそのまま使用されます。

以下は、ハッシュ テーブルを使用して循環参照の問題を解決するコード例です。

function deepCopy(obj, hash = new WeakMap()) {
    
    
    // 如果是基本类型或 null,则直接返回
    if (obj === null || typeof obj !== 'object') {
    
    
        return obj;
    }

    // 如果是已经拷贝过的对象,则返回拷贝的引用
    if (hash.has(obj)) {
    
    
        return hash.get(obj);
    }

    // 根据对象类型进行拷贝
    let newObj = Array.isArray(obj) ? [] : {
    
    };

    // 将当前对象存储到哈希表中
    hash.set(obj, newObj);

    // 递归拷贝对象的每个属性
    for (let key in obj) {
    
    
        if (obj.hasOwnProperty(key)) {
    
    
            newObj[key] = deepCopy(obj[key], hash);
        }
    }

    return newObj;
}

この例では、ES6 によって提供される WeakMap が、コピーされたオブジェクトを保存するためのハッシュ テーブルとして使用されます。deepCopy(obj)使用する場合は、ディープコピーを実行するメソッドを呼び出すだけで、完全に独立したコピーオブジェクトが返されるため、循環参照の問題が解決されます。この例は循環参照の問題を解決する 1 つの方法にすぎず、具体的な実装は言語やシナリオによって異なる場合があることに注意してください。

おすすめ

転載: blog.csdn.net/m0_49768044/article/details/132462870