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

Conseils JavaScript de base (tutoriels image et texte, réponses détaillées pour vous)

亚连
Libérer: 2018-05-18 16:50:58
original
1250 Les gens l'ont consulté

Avant-propos

En ce qui concerne la copie profonde et superficielle, nous devons d'abord mentionner les types de données de JavaScript L'article précédent Bases de JavaScript - Types de données l'a dit très clairement, pas. beaucoup à dire 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

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

Le code ci-dessus est le moyen le plus simple d'utiliser l'opérateur d'affectation = pour implémenter une copie superficielle. On peut clairement voir qu'à mesure que cloneArray et cloneObj changent, originArray et originObj changent également.

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'y a pas beaucoup de façons d'implémenter la copie profonde à l'heure actuelle, il existe principalement deux méthodes :

Utilisez parse et stringify dans l'objet JSON

Utilisez la récursivité pour recréer chaque couche Objet et attribuer une valeur

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 une 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 ou l'objet JavaScript décrit par la chaîne.

JSON.parse consiste à convertir 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 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"}
Copier après la connexion

Il s'avère que certains attributs manquent dans cloneObj. . . Pourquoi?

Trouvé la raison sur MDN :

Si une fonction ou un symbole non défini est rencontré lors de la conversion, il est soit omis (lorsqu'il est trouvé dans un objet), soit censuré à null (lorsque il se trouve dans un tableau). JSON.stringify peut également simplement renvoyer undefined lors du passage de valeurs "pures" comme JSON.stringify(function(){}) ou JSON.stringify(undefined).

undefined , fonction, symbole seront ignorés pendant le processus de 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 approfondie.

Méthode récursive

L'idée de la récursion est très simple. Il s'agit de créer un objet une fois pour chaque couche de données -> opération d'affectation d'objet :

.
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'}};
可以。那再试试带有函数的:
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

Aussi. 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 de ces deux méthodes ne modifiera le tableau d'origine. , renvoyant à la place un nouveau tableau modifié.

Dans le même temps, ES6 a introduit la méthode Object.assgn et l'opérateur... spread pour copier également des objets.

S'agit-il de copies superficielles ou de copies profondes ?

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 tableau.

Le La méthode peut concaténer deux ou plusieurs tableaux, 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];
Copier après la connexion

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

originArray contient le tableau [1,2,3] et l'objet {a:1} Si nous modifions directement le tableau et l'objet, cela n'affectera pas originArray, mais nous modifions le tableau [1, 2, 3] ou l'objet {a:1}, on constate que originArray a également changé.

Conclusion : concat ne fait qu'une copie complète du premier niveau du tableau.

slice

La méthode slice() renvoie une copie superficielle d'une partie d'un tableau dans un nouvel objet tableau sélectionné du début à la fin (la fin n'est pas incluse). modifié.

L'explication indique directement qu'il s'agit d'une copie superficielle~

Mais ce n'est pas le cas !

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

De même, essayons un tableau à plusieurs niveaux.

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

Effectivement, le résultat est le même que concat.

Conclusion : slice ne fait qu'une copie complète du premier niveau du tableau.

Object.assign()

La méthode Object.assign() est utilisée pour copier les valeurs de toutes les propriétés propres énumérables d'un ou plusieurs objets source vers un objet cible. renvoie l'objet cible.

Copier copie copie.

Est-ce une copie superficielle ou une copie profonde ?

Essayez-le vous-même. .

结论: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 中的一个对象被修改了。。。说好的深拷贝呢?不是引用地址都不一样了吗?

原来是这样:

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

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

总结

赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值;

JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;

JSON.stringify 实现的是深拷贝,但是对目标对象有要求;

若想真正意义上的深拷贝,请递归。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

用p5.js制作烟花特效的示例代码_javascript技巧

JS+PHP往类动态添加方法

怎样用JS检测电脑配置

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!