今回は、JSの深いコピーと浅いコピーを使用する手順について詳しく説明します。 JSの深いコピーと浅いコピーを使用する際の注意事項は何ですか?実際のケースを見てみましょう。
深いコピーと浅いコピーについて言えば、まず JavaScript の データ型 について言及する必要があります。前回の記事「JavaScript の基礎 - データ型」で非常にわかりやすく説明されているため、ここでは詳しく説明しません。
知っておくべきことは 1 つあります。JavaScript データ型は基本データ型と参照データ型に分類されるということです。
基本データ型のコピーの場合、ダーク コピーとシャロー コピーと呼ばれるものはすべて参照データ型です。
浅いコピー
浅いコピーとは、参照のみがコピーされ、実際の値はコピーされないことを意味します。
りー
上記のコードは、= 代入 演算子 を使用した浅いコピーの最も単純な実装です。cloneArray
和 cloneObj
改变,originArray
和 originObj
で変更されることがはっきりとわかります。
ディープコピー
深いコピーは、参照のレイヤーのみをコピーする浅いコピーとは異なり、ターゲットの完全なコピーです。値もコピーされます。
ディープコピーが作成されている限り、それらは相互に対話することはなく、誰も他の人に影響を与えることはありません。
現在、ディープコピーを実装する方法はそれほど多くはありませんが、主に 2 つの方法があります:
JSON オブジェクトで解析と文字列化を利用する
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'}};
undefined
、function
、symbol
試してみましょう: 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 中的一个对象被修改了。。。说好的深拷贝呢?不是引用地址都不一样了吗?
原来是这样:
从 shallowClone 的代码中我们可以看出,我们只对第一层的目标进行了 深拷贝 ,而第二层开始的目标我们是直接利用 = 赋值操作符进行拷贝的。
总结
代入演算子 = オブジェクトの参照値のみをコピーする浅いコピーを実装します。
この記事の事例を読んだ後は、この方法を習得したと思います。さらに興味深い情報については、php 中国語 Web サイトの他の関連記事に注目してください。
推奨読書:
Vue2.0のhttpリクエストの使用と読み込み表示の詳細な説明
ノードのプロセスとchild_processモジュールの使用の詳細な説明
以上がJSの深いコピーと浅いコピーを使用する手順の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。