ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript トピック 5: 深いコピーと浅いコピー

JavaScript トピック 5: 深いコピーと浅いコピー

coldplay.xixi
coldplay.xixi転載
2021-03-08 09:36:141999ブラウズ

JavaScript トピック 5: 深いコピーと浅いコピー

#コピーのプロセスを理解して不要なミスを回避しましょう。Js 特別シリーズの深いコピーと浅いコピーを一緒に頑張りましょう~

目次

    1. コピー例
  • 2. 浅いコピー
  • 3. 深いコピーの方法は?
  • 4. 深いコピーと浅いコピーを自分で実装する

無料学習の推奨事項: javascriptビデオチュートリアル

1. 例をコピー

#データを操作すると、次のような状況が発生する可能性があります:

    データのセットは頻繁に変更されますが、元のデータが使用される可能性があります
  1. 同じデータのセットが 2 つ必要ですが、1 つを変更したくないので、もう 1 つが変更されます
  2. #I 手術前後のデータを比較する必要があります
  3. #…
  4. 同じようなニーズのシナリオに遭遇したとき、最初に思い浮かぶのは次のようなことです。コピーすることはできますが、コピーすることにも多くの知識が必要であることは知りません~
次の簡単な例に聞き覚えはありますか?

1.1 基本型のコピー例

var str = 'How are you';var newStr = str;newStr = 10console.log(str); // How are youconsole.log(newStr); // 10
誰もが想像できるように、string は基本型であり、その値はスタックに格納されます。これをコピーすると、実際には新しい変数が開かれます。新しい空間。
str

newStr は、レイアウトは同じですが接続されていない 2 つの同一の部屋のようなものです。 1.2 参照型のコピー例

var data = [1, 2, 3, 4, 5];var newData = data;newData[0] = 100;console.log(data[0]); // 100console.log(newData[0]); // 100
同様のコードセグメントですが、今回は配列の参照型を例として使用します。割り当てられたデータを変更すると、元のデータが変化していることがわかります。これは明らかに私たちのニーズを満たしていません。この記事では、
引用データのコピー
に関する知識について説明します。

J のデータ型について質問がある場合は、「JavaScript の基本データ型」を参照してください。

JavaScript トピック 5: 深いコピーと浅いコピー2. 浅いコピー

コピーの分割は参照型に基づいて説明されます。浅いコピー - 名前が示すように、浅いコピーは「浅いコピー」です。表面的な作業のみを行います:

var arr = [1, 2, 3, 4];var newArr = arr;console.log(arr, newArr); // [1,2,3,4] [1,2,3,4]newArr[0] = 100;console.log(arr, newArr) // [100,2,3,4] [100,2,3,4]
幸いなことに、何も起こりません (操作) 新しい配列が操作されると、両方の変数に格納されているデータが変更されます。

このような状況が発生する理由は、

参照型

の基本的な特性によるものでもあります。

変数に格納される値はポインタ ( point)、ストレージを指すオブジェクトのメモリ アドレス。新しい変数に値を割り当てることは、新しいキーを割り当てることと同じであり、部屋は変更されません。

  • 配列のスライスと連結は両方とも新しい配列を返します。一緒に試してみましょう:
var arr = [1,2,3,4];var res = arr.slice();// 或者res = arr.concat()res[0] = 100;console.log(arr); // [1,2,3,4]
この問題はすぐに解決しましたか?このデータ層はこのように処理されるため、確かに問題は解決されますが、しかし!

var arr = [ { age: 23 }, [1,2,3,4] ];var newArr = arr.concat();arr[0].age = 18;arr[1][0] = 100;console.log(arr) // [ {age: 18}, [100,2,3,4] ]console.log(newArr) // [ {age: 18}, [100,2,3,4] ]
確かに、物事はそれほど単純ではありません。これは、データ型が異なるためでもあります。

S メモリ内のアドレスを直接操作することは許可されていないため、オブジェクトのメモリ空間を操作できないため、オブジェクトに対する操作はその参照に対してのみ操作されます。

効率

の原則に従って、

浅いコピー

では私たちの要件を満たせないため、##の達成を手伝ってくれる人がいるかどうかを探しています。 #ディープコピー メソッド。

#3. ディープコピーをするにはどうすればよいですか?

JavaScript トピック 5: 深いコピーと浅いコピー

データ メソッドが失敗しました。他に方法はありますか?独立したデータの真のコピーを作成する必要があります。

3.1 JSON

ここでは、最も簡潔な処理を実現するために、JSON の 2 つのメソッド

JSON.stringify()

JSON.parse()
を使用します。ディープ コピー

var arr = ['str', 1, true, [1, 2], {age: 23}]var newArr = JSON.parse( JSON.stringify(arr) );newArr[3][0] = 100;console.log(arr); // ['str', 1, true, [1, 2], {age: 23}]console.log(newArr); // ['str', 1, true, [100, 2], {age: 23}]
この方法はディープ コピーを実装する最も簡単な方法のはずですが、まだ問題があります。今行ったことを見てみましょう:

すべての型を含む配列を定義しますarr

    JSON.stringify(arr)、JavaScript オブジェクトまたは値を
  1. JSON 文字列に変換します
  2. JSON.parse(xxx) メソッドは、JSON 文字列を解析し、文字列で記述された
  3. 値またはオブジェクトを構築するために使用されます。
  4. 理解 :
元のデータを

new string に変換し、new string

## を介して

new object# に復元するという変更方法であることがわかります。データ型はオブジェクト参照のコピープロセスを間接的にバイパスし、元のデータには影響を与えません。 制限事項: このメソッドの基本的な目的は、「転送」中のデータの整合性を確保することであり、

JSON.stringify()

対応する JSON 形式 :

への値の変換にも欠陥があります
  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。
  • 函数、undefined 被单独转换时,会返回 undefined,
    • 如JSON.stringify(function(){})
    • JSON.stringify(undefined)
  • 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  • NaN 和 Infinity 格式的数值及 null 都会被当做 null。
  • 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。

所以当我们拷贝函数、undefined等stringify转换有问题的数据时,就会出错,我们在实际开发中也要结合实际情况使用。

举一反三:

既然是通过改变数据类型来绕过拷贝引用这一过程,那么单纯的数组深拷贝是不是可以通过现有的几个API来实现呢?

var arr = [1,2,3];var newArr = arr.toString().split(',').map(item => Number(item))newArr[0] = 100;console.log(arr); // [1,2,3]console.log(newArr); // [100,2,3]

注意,此时仅能对包含纯数字的数组进行深拷贝,因为:

  1. toString无法正确的处理对象和函数
  2. Number无法处理 false、undefined等数据类型

但我愿称它为纯数字数组深拷贝

JavaScript トピック 5: 深いコピーと浅いコピー

3.2 Object.assign()

有的人会认为Object.assign(),可以做到深拷贝,我们来看一下

var obj = {a: 1, b: { c: 2 } }var newObj = Object.assign({}, obj)newObj.a = 100;newObj.b.c = 200;console.log(obj); // {a: 1, b: { c: 200 } }console.log(newObj) // {a: 100, b: { c: 200 } }

神奇,第一层属性没有改变,但第二层却同步改变了,这是为什么呢?

因为 Object.assign()拷贝的是(可枚举)属性值。

假如源值是一个对象的引用,它仅仅会复制其引用值。MDN传送门

四、自己实现深浅拷贝

既然现有的方法无法实现深拷贝,不妨我们自己来实现一个吧~

4.1 浅拷贝

我们只需要将所有属性即其嵌套属性原封不动的复制给新变量一份即可,抛开现有的方法,我们应该怎么做呢?

var shallowCopy = function(obj) {
    if (typeof obj !== 'object') return;

    // 根据obj的类型判断是新建一个数组还是对象
    var newObj = obj instanceof Array ? [] : {};
    // 遍历obj,并且判断是obj的属性才拷贝
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;}

我们只需要将所有属性的引用拷贝一份即可~

4.2 深拷贝

相信大家在实现深拷贝的时候都会想到递归,同样是判断属性值,但如果当前类型为object则证明需要继续递归,直到最后

var deepCopy = function(obj) {
    if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;}

我们用白话来解释一下deepCopy都做了什么

const obj = [1, { a: 1, b: { name: '余光'} } ];const resObj = deepCopy(obj);
  • 读取 obj,创建 第一个newObj
    • 判断类型为 []
    • key为 0 (for in 以任意顺序遍历,我们假定按正常循序遍历)
    • 判断不是引用类型,直接复制
    • key为 1
    • 判断是引用类型
    • 进入递归,重新走了一遍刚才的流程,只不过读取的是obj[1]

另外请注意递归的方式虽然可以深拷贝,但是在性能上肯定不如浅拷贝,大家还是需要结合实际情况来选择。

写在最后

JavaScript トピック 5: 深いコピーと浅いコピー

前端专项进阶系列的第五篇文章,希望它能对大家有所帮助,如果大家有什么建议,可以在评论区留言,能帮到自己和大家就是我最大的动力!

相关免费学习推荐:javascript(视频)

以上がJavaScript トピック 5: 深いコピーと浅いコピーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。