JavaScript のディープコピーの落とし穴

JavaScript のディープコピーの落とし穴

序文

以前、ある会社の面接に行ったとき、面接官から「オブジェクトを深くコピーするにはどうすればよいですか?」という質問を受けました。その時、私は密かに嬉しく思いました。こんな単純な疑問を考える必要があるのでしょうか?そこで私はこう言いました。「よく使われる方法は 2 つあります。1 つ目は JSON.parse(JSON.stringify(obj)) を使用する方法で、2 つ目は for...in と再帰を使用する方法です。」これを聞いた面接官はうなずき、満足そうでした。
当時はこの問題についてあまり気にしていませんでしたが、しばらく前にもう一度考えてみると、上記の両方の方法にバグがあることがわかりました。

質問する

では、上で述べたバグとは何でしょうか?

特殊オブジェクトのコピー

まず、共通の型を考慮せずに、次のメンバーを持つオブジェクトを想像してみましょう。

定数オブジェクト = {
    編曲: [111, 222],
    obj: {キー: 'オブジェクト'},
    a: () => {console.log('function')},
    日付: 新しい Date(),
    登録: /regular/ig
}

次に、上記の2つの方法をそれぞれ使用してコピーします。

JSON メソッド

JSON.parse(JSON.stringify(obj))

出力:

obj 内の通常のオブジェクトと配列の両方をコピーできることがわかりますが、日付オブジェクトは文字列になり、関数は直接消え、正規表現は空のオブジェクトになります。
再帰を使ったfor...inメソッドを見てみましょう

再帰

関数isObj(obj) {
    戻り値 (typeof obj === 'オブジェクト' || typeof obj === '関数') && obj !== null
}
関数 deepCopy(obj) {
    tempObj = Array.isArray(obj) ? [] : {} とします。
    for(let key in obj) {
        tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
    }
    tempObjを返す
}

結果:

結論は

上記のテストから、これら 2 つのメソッドは function、date、reg タイプのオブジェクトをコピーできないことがわかります。

  • 指輪

リングとは何ですか?

ループはオブジェクト間の循環参照であり、閉じたループになります。たとえば、次のオブジェクトがあります。

var a = {}

aa = ア

上記の2つの方法でコピーすると、エラーが直接報告されます。

解決

  • 指輪

コピーされたオブジェクトを格納するために WeakMap 構造体を使用することができます。オブジェクトをコピーするたびに、WeakMap を照会して、オブジェクトがコピーされたかどうかを確認します。コピーされた場合は、オブジェクトを取り出して返します。deepCopy 関数を次のように変換します。

関数 deepCopy(obj, hash = new WeakMap()) {
    hash.has(obj) の場合、hash.get(obj) を返します。
    cloneObj = Array.isArray(obj) ? [] : {} とします。
    ハッシュを設定します(obj、クローンObj)
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }
    cloneObjを返す
}

リング結果をコピー:

特殊オブジェクトのコピー

この問題の解決法は、特別に扱う必要があるオブジェクトの種類が多すぎるため、かなり複雑です。そこで、MDN の構造化コピーを参照し、それをリングの解決法と組み合わせました。

// 日付と登録型のみを解決し、他の型は自分で追加できます。 function deepCopy(obj, hash = new WeakMap()) {
    cloneObj を作ろう
    コンストラクタ = obj.constructor
    switch(コンストラクタ){
        ケース正規表現:
            cloneObj = 新しいコンストラクタ(obj)
            壊す
        事件日:
            cloneObj = 新しいコンストラクタ(obj.getTime())
            壊す
        デフォルト:
            hash.has(obj) の場合、hash.get(obj) を返します。
            cloneObj = 新しいコンストラクタ()
            ハッシュを設定します(obj、クローンObj)
    }
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }
    cloneObjを返す
}

コピー結果:

完全版については、lodash deep copyをご覧ください。

  • 関数のコピー

ただし、MDN の構造化コピーでは、関数のコピーの問題は依然として解決されません。

これまでは関数をコピーするために eval メソッドを使うことしか考えていませんでしたが、この方法はアロー関数にしか機能しません。fun(){} の形式だと失敗します。

関数をコピーして関数タイプを追加する

結果をコピー

エラーの種類

追記

JavaScript のディープ コピーには、上記で説明した問題以外にも多くの問題があります。もう 1 つの問題は、プロトタイプ チェーン上のプロパティをどのようにコピーするかということです。列挙不可能なプロパティをコピーする方法、エラー オブジェクトをコピーする方法などについては、ここでは詳しく説明しません。

ただし、日常生活では依然として JSON メソッドを使用することをお勧めします。このメソッドはほとんどのビジネス ニーズをカバーしているため、単純なことを複雑にする必要はありません。ただし、面接中に細かいことを気にする面接官に遭遇した場合、この質問に対する答えは間違いなく面接官の印象を良くするでしょう。

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

以下もご興味があるかもしれません:
  • JavaScriptの浅いコピーと深いコピーについての簡単な説明
  • js における浅いコピーと深いコピーの詳細な説明
  • JS変数ストレージのディープコピーとシャローコピーの詳しい説明
  • JS オブジェクトのコピー (ディープ コピーとシャロー コピー)
  • JSのディープコピーとシャローコピーの詳しい説明
  • jsのディープコピーを理解しましょう

<<:  alpineをベースにdockerfileで作成したクローラーScrapyイメージの実装

>>:  mysql mycat ミドルウェアのインストールと使用

推薦する

ウェブページのテーブルの境界線を設定する方法

<br />前回は、Web テーブルにセルの線を設定する方法を学びました。今日は、Web...

MySQLバイナリログを介してデータベースデータを復元する方法の詳細な説明

ウェブサイト管理者は、さまざまな理由や操作により、ウェブサイトのデータを誤って削除したり、ウェブサイ...

Linuxファイルシステム操作の実装

この読書ノートでは、主にファイルシステムに関連する操作を記録します。ディスクとディレクトリの容量ディ...

CSSコンテンツ属性の具体的な使用法

コンテンツ属性は通常、::before および ::after 疑似要素で使用され、疑似要素のコンテ...

WeChat アプレットカスタムタブバーステップ記録

目次1. はじめに2. タブバーのスタイルをカスタマイズする3. カスタムタブバーと関連設定を導入す...

mysql mycat ミドルウェアの簡単な紹介

1. mycatとはエンタープライズアプリケーション開発のための完全にオープンソースの大規模データベ...

JS デコレータ パターンと TypeScript デコレータ

目次デコレータパターンの紹介TypeScript のデコレータデコレータの使用デコレーターファクトリ...

マウスをホバーすると画像が折りたたまれる効果を実現する CSS

マウスをホバーすると画像が折りたたまれる効果を実現する CSS 1. 実施のポイント折り畳みは複数の...

MySQL マスタースレーブレプリケーション構成プロセス

メインライブラリの構成1. MySQLを設定する vim /etc/my.cn # ファイルに次の内...

Ubuntu 16.04 64 ビット版の VMware Tools のインストールと構成のグラフィック チュートリアル

この記事では、VMware Toolsのインストールと構成に関するグラフィックチュートリアルを参考と...

MySQL テーブルにおける非主キー列オーバーフロー監視の詳細な説明

今日もまた罠に落ちてしまいました。 私は以前MySQLの主キーオーバーフローに遭遇したことがあり、そ...

MySQL のインストール方法と設定に関するいくつかの問題の概要

1. MySQL rpm パッケージのインストール # インストールソースをダウンロードします [r...

CentOS7にPostgreSQL11をインストールする方法

CentOS 7にPostgreSQL 11をインストールする PostgreSQL: 世界で最も先...

Docker での FastAPI デプロイの詳細なプロセス

Docker 学習https://www.cnblogs.com/poloyy/p/15257059...

まだ*を選択しますか?

アプリケーションが牛のように遅い理由は数多くあります。ネットワーク、システム アーキテクチャ、または...