ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript トピック 5: 深いコピーと浅いコピー
#コピーのプロセスを理解して不要なミスを回避しましょう。Js 特別シリーズの深いコピーと浅いコピーを一緒に頑張りましょう~
目次
無料学習の推奨事項: javascriptビデオチュートリアル
1. 例をコピー
#データを操作すると、次のような状況が発生する可能性があります:var str = 'How are you';var newStr = str;newStr = 10console.log(str); // How are youconsole.log(newStr); // 10誰もが想像できるように、string は基本型であり、その値はスタックに格納されます。これをコピーすると、実際には新しい変数が開かれます。新しい空間。
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 の基本データ型」を参照してください。
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.1 JSON
ここでは、最も簡潔な処理を実現するために、JSON の 2 つのメソッドJSON.stringify()
、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
理解 :
new string に変換し、new string
## を介してnew object# に復元するという変更方法であることがわかります。データ型はオブジェクト参照のコピープロセスを間接的にバイパスし、元のデータには影響を与えません。 制限事項:
このメソッドの基本的な目的は、「転送」中のデータの整合性を確保することであり、
対応する JSON 形式 :
への値の変換にも欠陥があります所以当我们拷贝函数、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]
注意,此时仅能对包含纯数字的数组进行深拷贝,因为:
但我愿称它为纯数字数组深拷贝!
有的人会认为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传送门
四、自己实现深浅拷贝
既然现有的方法无法实现深拷贝,不妨我们自己来实现一个吧~
我们只需要将所有属性即其嵌套属性原封不动的复制给新变量一份即可,抛开现有的方法,我们应该怎么做呢?
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;}
我们只需要将所有属性的引用拷贝一份即可~
相信大家在实现深拷贝的时候都会想到递归,同样是判断属性值,但如果当前类型为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[]
0
(for in
以任意顺序遍历,我们假定按正常循序遍历)1
obj[1]
另外请注意递归的方式虽然可以深拷贝,但是在性能上肯定不如浅拷贝,大家还是需要结合实际情况来选择。
写在最后
前端专项进阶系列的第五篇文章
,希望它能对大家有所帮助,如果大家有什么建议,可以在评论区留言,能帮到自己和大家就是我最大的动力!
相关免费学习推荐:javascript(视频)
以上がJavaScript トピック 5: 深いコピーと浅いコピーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。