JSの深いコピーと浅いコピーを使用する手順の詳細な説明

php中世界最好的语言
リリース: 2018-04-12 10:52:34
オリジナル
1424 人が閲覧しました

今回は、JSの深いコピーと浅いコピーを使用する手順について詳しく説明します。 JSの深いコピーと浅いコピーを使用する際の注意事項は何ですか?実際のケースを見てみましょう。

深いコピーと浅いコピーについて言えば、まず JavaScriptデータ型 について言及する必要があります。前回の記事「JavaScript の基礎 - データ型」で非常にわかりやすく説明されているため、ここでは詳しく説明しません。

知っておくべきことは 1 つあります。JavaScript データ型は基本データ型と参照データ型に分類されるということです。

基本データ型のコピーの場合、ダーク コピーとシャロー コピーと呼ばれるものはすべて参照データ型です。

浅いコピー

浅いコピーとは、参照のみがコピーされ、実際の値はコピーされないことを意味します。

りー

上記のコードは、= 代入 演算子 を使用した浅いコピーの最も単純な実装です。cloneArraycloneObj 改变,originArray originObj で変更されることがはっきりとわかります。

ディープコピー

深いコピーは、参照のレイヤーのみをコピーする浅いコピーとは異なり、ターゲットの完全なコピーです。値もコピーされます。

ディープコピーが作成されている限り、それらは相互に対話することはなく、誰も他の人に影響を与えることはありません。

現在、ディープコピーを実装する方法はそれほど多くはありませんが、主に 2 つの方法があります:

  1. JSON オブジェクトで解析と文字列化を利用する

  2. 再帰を使用してオブジェクトを再作成し、各レイヤーで値を割り当てる

JSON.stringify/parseメソッド

まずはこれら 2 つの方法を見てみましょう:

JSON.stringify() メソッドは、JavaScript 値を JSON 文字列に変換します。

は、JavaScript 値を JSON 文字列に変換することです。

JSON.parse() メソッドは JSON 文字列を解析し、文字列で記述された JavaScript 値またはオブジェクトを構築します。 JSON.stringify JSON 文字列を JavaScript 値またはオブジェクトに変換します。

分かりやすいと、JavaScriptの値とJSON文字列の変換です。

ディープコピーは実現できるのでしょうか?試してみよう。
りー

確かにディープコピーでとても便利です。ただし、この方法はいくつかの単純な状況にのみ適用できます。たとえば、次のオブジェクトは適用されません: JSON.parse

const originArray = [1,2,3,4,5];
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneArray = originArray;
const cloneObj = originObj;
console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}
cloneArray.push(6);
cloneObj.a = {aa:'aa'};
console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]
console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
ログイン後にコピー

にいくつかの属性が欠落していることがわかりました。 。 。なぜ?

MDN で理由を見つけました:

未定義の場合、実行中に関数またはシンボルが検出されます。 変換では、(オブジェクト内で見つかった場合) 省略されるか、 JSON.stringify を null に打ち切ることもできます。 次のような「純粋な」値を渡す場合は、unknown を返すだけです JSON.stringify(function(){}) または JSON.stringify(未定義)。

は変換中に無視されます。 。 。 cloneObj

つまり、オブジェクトに関数が含まれている場合 (非常に一般的)、このメソッドを使用してディープ コピーを実行することはできません。

再帰的メソッド

再帰の考え方は非常に単純で、データの各層に対してオブジェクトの作成 -> オブジェクトの割り当てという操作を実装することです。コードは単純かつ粗雑です:

const originArray = [1,2,3,4,5];
const cloneArray = JSON.parse(JSON.stringify(originArray));
console.log(cloneArray === originArray); // false
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
ログイン後にコピー
undefinedfunctionsymbol 試してみましょう: rreee

できる。次に、関数を備えたものを試してください:

const originObj = {
 name:'axuebin',
 sayHello:function(){
 console.log('Hello World');
 }
}
console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj); // {name: "axuebin"}
ログイン後にコピー

それも大丈夫です。終わり。 これで終わりだと思いますか? ? もちろん違います。

JavaScriptでメソッドをコピー

我们知道在 JavaScript 中,数组有两个方法 concat 和 slice 是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组。

同时,ES6 中 引入了 Object.assgn 方法和 ... 展开运算符也能实现对对象的拷贝。

那它们是浅拷贝还是深拷贝呢?

concat

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

该方法可以连接两个或者更多的数组,但是它不会修改已存在的数组,而是返回一个新数组。

看着这意思,很像是深拷贝啊,我们来试试:

const originArray = [1,2,3,4,5];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
ログイン後にコピー

看上去是深拷贝的。

我们来考虑一个问题,如果这个对象是多层的,会怎样。

const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]
ログイン後にコピー

originArray 中含有数组 [1,2,3] 和对象 {a:1},如果我们直接修改数组和对象,不会影响 originArray,但是我们修改数组 [1,2,3] 或对象 {a:1} 时,发现 originArray 也发生了变化。

结论:concat 只是对数组的第一层进行深拷贝。

slice

The slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.

解释中都直接写道是 a shallow copy 了 ~

但是,并不是!

const originArray = [1,2,3,4,5];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
ログイン後にコピー

同样地,我们试试多层的数组。

const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]
ログイン後にコピー

果然,结果和 concat 是一样的。

结论:slice 只是对数组的第一层进行深拷贝。

Object.assign()

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

复制复制复制。

那到底是浅拷贝还是深拷贝呢?

自己试试吧。。

结论:Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。

... 展开运算符

const originArray = [1,2,3,4,5,[6,7,8]];
const originObj = {a:1,b:{bb:1}};
const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(originArray); // [1,2,3,4,5,[6,7,8,9]]
const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}
ログイン後にコピー

结论:... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。

首层浅拷贝

我们知道了,会有一种情况,就是对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。

我们可以自己实现一个这样的函数:

function shallowClone(source) {
 const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
 for (let keys in source) { // 遍历目标
 if (source.hasOwnProperty(keys)) {
 targetObj[keys] = source[keys];
 }
 }
 return targetObj;
}
ログイン後にコピー

我们来测试一下:

const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = shallowClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a='aa';
cloneObj.c=[1,1,1];
cloneObj.d.dd='surprise';
ログイン後にコピー

经过上面的修改,cloneObj 不用说,肯定是 {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}} 了,那 originObj 呢?刚刚我们验证了 cloneObj === originObj 是 false,说明这两个对象引用地址不同啊,那应该就是修改了 cloneObj 并不影响 originObj。

console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'surprise'}}
ログイン後にコピー

What happend?

originObj 中关于 a、c都没被影响,但是 d 中的一个对象被修改了。。。说好的深拷贝呢?不是引用地址都不一样了吗?

原来是这样:

  1. 从 shallowClone 的代码中我们可以看出,我们只对第一层的目标进行了 深拷贝 ,而第二层开始的目标我们是直接利用 = 赋值操作符进行拷贝的。

  2. so,第二层后的目标都只是复制了一个引用,也就是浅拷贝。

总结

  1. 代入演算子 = オブジェクトの参照値のみをコピーする浅いコピーを実装します。

  2. JavaScript の配列とオブジェクトのコピー メソッドはすべて「第 1 レベルの浅いコピー」です

  3. JSON.stringifyこれはディープ コピーですが、ターゲット オブジェクトに関する要件があります

  4. 真のディープ コピーが必要な場合は、再帰してください。

この記事の事例を読んだ後は、この方法を習得したと思います。さらに興味深い情報については、php 中国語 Web サイトの他の関連記事に注目してください。

推奨読書:

Vue2.0のhttpリクエストの使用と読み込み表示の詳細な説明

ノードのプロセスとchild_processモジュールの使用の詳細な説明

以上がJSの深いコピーと浅いコピーを使用する手順の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート