Maison > interface Web > js tutoriel > le corps du texte

Explication détaillée des étapes d'utilisation de la copie profonde et superficielle de JS

php中世界最好的语言
Libérer: 2018-04-12 10:52:34
original
1413 Les gens l'ont consulté

Cette fois, je vais vous donner une explication détaillée des étapes d'utilisation de la copie profonde et superficielle de JS. Quelles sont les précautions lors de l'utilisation de la copie profonde et superficielle de JS. , jetons un coup d'oeil.

En parlant de copie profonde et superficielle, nous devons d'abord mentionner le type de données de JavaScript Un article précédent parlait des bases de JavaScript - les types de données. c'est clair, je n'en dirai pas beaucoup plus 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 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 toutes destinées aux types de données de référence.

Copie superficielle

Une copie superficielle signifie que seule la référence est copiée, mais que 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'}}
Copier après la connexion

Le code ci-dessus est l'utilisation la plus simple de l'opérateur = assignation pour implémenter une copie superficielle. On voit clairement que lorsque cloneArray et cloneObj changent, originArray et originObj changent également. les choses ont changé.

Copie approfondie

Une copie profonde est une copie complète de la cible, contrairement à une copie superficielle, qui copie uniquement une couche de références, même les valeurs sont copiées.

Tant qu'une copie complète est effectuée, ils n'interagiront jamais les uns avec les autres et personne n'affectera personne d'autre.

Actuellement, il n'existe pas beaucoup de façons de mettre en œuvre la copie profonde, il existe principalement deux méthodes :

  1. Utiliser l'analyse et la chaîne dans les objets JSON

  2. Utiliser la récursivité pour créer des objets à nouveau à chaque niveau et attribuer des valeurs

Méthode JSON.stringify/parse

Jetons d'abord un coup d'œil à ces deux méthodes :

La méthode JSON.stringify() convertit une valeur JavaScript en chaîne JSON

. JSON.stringify convertit une valeur JavaScript en chaîne JSON.

La méthode JSON.parse() analyse une chaîne JSON, en construisant la valeur JavaScript ou l'objet décrit par la chaîne

. JSON.parse convertit une chaîne JSON en une valeur ou un objet JavaScript.

C'est facile à comprendre, c'est la conversion de valeurs JavaScript et de chaînes JSON.

Peut-il réaliser une copie approfondie ? Essayons.

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'}};
Copier après la connexion

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 ne s'applique pas :

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"}
Copier après la connexion

J'ai constaté que certains attributs manquaient dans cloneObj . . . Pourquoi?

J'ai trouvé la raison sur MDN :

S'il n'est pas défini, une fonction ou un symbole est rencontré pendant conversion, il est soit omis (lorsqu'il est trouvé dans un objet), soit censuré à null (lorsqu'il est trouvé dans un tableau, JSON.stringify peut également). retournez simplement undéfini lors du passage de valeurs "pures" comme JSON.stringify(function(){}) ou JSON.stringify(undefined

). undefined, function, symbol seront ignorés lors de la conversion. . .

Comprenez, c’est-à-dire que si l’objet contient une fonction (très courante), vous ne pouvez pas utiliser cette méthode pour effectuer une copie complète.

Méthode récursive

L'idée de la récursion est très simple. Il s'agit de mettre en œuvre l'opération de création d'un objet -> affectation d'objet pour chaque couche de données. Le code est simple et brut :

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;
}
Copier après la connexion

. 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'}};
Copier après la connexion

Peut. Alors essayez celui avec 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: ƒ}
Copier après la connexion

C'est ok aussi. Fait.

Pensez-vous que c'est la fin ? ? Bien sûr que non.

Méthode de copie en 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];
Copier après la connexion

看上去是深拷贝的。

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

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}]
Copier après la connexion

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];
Copier après la connexion

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

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}]
Copier après la connexion

果然,结果和 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}}
Copier après la connexion

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

首层浅拷贝

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

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

function shallowClone(source) {
 const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
 for (let keys in source) { // 遍历目标
 if (source.hasOwnProperty(keys)) {
 targetObj[keys] = source[keys];
 }
 }
 return targetObj;
}
Copier après la connexion

我们来测试一下:

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';
Copier après la connexion

经过上面的修改,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'}}
Copier après la connexion

What happend?

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

原来是这样:

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

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

总结

  1. Opérateur d'affectation = implémente une copie superficielle, qui copie uniquement la valeur de référence de l'objet ;

  2. Les méthodes de copie de tableaux et d'objets en JavaScript sont toutes des « copies superficielles de premier niveau ; " ;

  3. JSON.stringify implémente la copie complète, mais il a des exigences pour l'objet cible ;

  4. Si vous voulez une véritable copie complète, S'il vous plaît, récurez-vous.

Je pense que vous maîtrisez la méthode après avoir lu le cas dans cet article. Pour des informations plus intéressantes, veuillez prêter attention aux autres articles connexes sur le site Web chinois de php !

Lecture recommandée :

Explication détaillée de la requête http de Vue2.0 et de l'utilisation de l'affichage de chargement

le processus du nœud et les modules child_process Explication détaillée de l'utilisation

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!