Maison > Article > interface Web > Bases de JavaScript Explication détaillée des problèmes entre la copie superficielle et la copie profonde
La copie superficielle et la copie profonde sont toutes deux destinées aux types de référence dans JS. La copie superficielle copie simplement la référence de l'objet. Si l'objet copié change, l'objet d'origine changera également. Seule la copie profonde est la copie réelle de l'objet
Préface
Quand il s'agit de copie profonde et superficielle, la première chose à mentionner est le type de données JavaScript, l'article précédent L'article "Basic JavaScript Mindset - Data Type" l'explique très clairement, je n'entrerai donc pas dans les détails ici.
Ce que vous devez savoir est une chose : les types de données JavaScript sont divisés en types de données de base et en types de données de référence.
Pour la copie des types de données de base, il n'y a aucune différence entre les copies sombres et superficielles. Ce que nous appelons les copies sombres et superficielles sont destinées aux types de données de référence.
Copie superficielle
Copie superficielle signifie que seule la référence est copiée, mais la valeur réelle n'est pas copiée.
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'}}
Le code ci-dessus est le moyen le plus simple d'utiliser l'opérateur d'affectation = pour implémenter une copie superficielle. Vous pouvez clairement le voir, avec le et cloneArray
changent, et cloneObj
et originArray
changent également. originObj
Copie profonde
La copie profonde est une copie complète de la cible, contrairement à la copie superficielle, qui copie uniquement une couche de références, même la valeur est copiée. Tant qu'une copie complète est effectuée, ils n'interagiront jamais les uns avec les autres et personne n'affectera l'autre. Il n'existe pas beaucoup de façons d'implémenter la copie profonde à l'heure actuelle, il existe principalement deux méthodes :Méthode JSON.stringify/parse
Regardez ce premier Deux méthodes : La méthode JSON.stringify() convertit une valeur JavaScript en chaîne JSON. consiste à convertir une valeur JavaScript en chaîne JSON. JSON.stringify
consiste à convertir une chaîne JSON en une valeur ou un objet JavaScript . JSON.parse
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'}};C'est en effet une copie profonde et très pratique. Toutefois, cette méthode ne peut être appliquée qu’à quelques situations simples. Par exemple, l'objet suivant n'est pas applicable :
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"}On constate que certains attributs manquent dans
. . . Pourquoi? cloneObj
, undefined
, function
seront ignorés pendant le processus de conversion. . . symbol
Méthode récursive
L'idée de la récursion est très simple, c'est-à-dire créer un objet -> opération d'affectation d'objet pour chaque couche de données, simple Rough code :function deepClone(source){ const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象 for(let keys in source){ // 遍历目标 if(source.hasOwnProperty(keys)){ if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下 targetObj[keys] = source[keys].constructor === Array ? [] : {}; targetObj[keys] = deepClone(source[keys]); }else{ // 如果不是,就直接赋值 targetObj[keys] = source[keys]; } } } return targetObj; }Essayons :
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}}; const cloneObj = deepClone(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'}};Oui. Alors essayez celui avec des fonctions :
const originObj = { name:'axuebin', sayHello:function(){ console.log('Hello World'); } } console.log(originObj); // {name: "axuebin", sayHello: ƒ} const cloneObj = deepClone(originObj); console.log(cloneObj); // {name: "axuebin", sayHello: ƒ}peut également être utilisé. Fait. Pensez-vous que c'est la fin ? ? Bien sûr que non.
Méthodes de copie en JavaScript
Nous savons qu'en JavaScript, les tableaux ont deux méthodes, concat et slice, qui peuvent copier le tableau d'origine. Aucune des méthodes. modifiera le tableau d'origine, mais renverra un nouveau tableau modifié. En même temps, ES6 introduit la méthode et l'opérateur... spread pour copier également des objets. Object.assgn
concat
La méthode concat() est utilisée pour fusionner deux ou plusieurs tableaux. Cette méthode ne modifie pas les tableaux existants, mais renvoie à la place un nouveau. array.
Cette méthode peut connecter deux tableaux ou plus, mais elle ne modifie pas le tableau existant, mais renvoie un nouveau tableau. On dirait que cela signifie une copie complète. Essayons :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];Cela ressemble à une copie complète. Considérons un problème, que se passera-t-il si cet objet est multicouche.
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}]
contient un tableau originArray
et un objet [1,2,3]
Si nous modifions directement le tableau et l'objet, cela n'affectera pas , mais lorsque l'on modifie le tableau {a:1}
ou l'objet originArray
, on constate que [1,2,3]
a également changé. {a:1}
结论: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 的代码中我们可以看出,我们只对第一层的目标进行了 深拷贝 ,而第二层开始的目标我们是直接利用 = 赋值操作符进行拷贝的。
so,第二层后的目标都只是复制了一个引用,也就是浅拷贝。
总结
赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值;
JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;
JSON.stringify 实现的是深拷贝,但是对目标对象有要求;
若想真正意义上的深拷贝,请递归。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!