Maison >interface Web >js tutoriel >Méthode de mise en œuvre des compétences JavaScript Deep Clone_Javascript

Méthode de mise en œuvre des compétences JavaScript Deep Clone_Javascript

WBOY
WBOYoriginal
2016-05-16 15:14:441386parcourir

Il existe un type de modèle de réutilisation de code appelé « modèle de copie de propriétés ». Lorsque nous pensons à la réutilisation du code, nous pensons très probablement à l’héritage du code, mais il est important de se rappeler l’objectif ultime : nous voulons réutiliser le code. L'héritage n'est qu'un moyen de réutiliser le code, pas le seul. La copie de propriétés est également un modèle de réutilisation, différent de l'héritage. Dans ce modèle, un objet obtiendra les membres d'un autre objet en les copiant simplement. Quiconque a utilisé jQuery sait qu'il dispose d'une méthode $.extend(). En plus d'étendre des plug-ins tiers, elle peut également être utilisée pour copier des attributs. Jetons un coup d'œil au code d'implémentation d'une fonction extend() (notez qu'il ne s'agit pas du code source de jQuery, juste un exemple simple) :

function extend(parent, child) {
var i;
//如果不传入第二参数child
//那么就创建一个新的对象
child = child || {}; 
//遍历parent对象的所有属性
//并且过滤原型上的属性
//然后将自身属性复制到child对象上
for(i in parent) {
if(parent.hasOwnProperty(i)) {
child[i] = parent[i];
}
}
//返回目标对象child
return child;
} 

Le code ci-dessus est une implémentation simple, il traverse uniquement les membres de l'objet parent et les copie dans l'objet enfant. Testons-le en utilisant la méthode extend() ci-dessus :

var dad = {name: "Adam"};
var kid = extend(dad);
console.log(kid.name); //Adam 

Nous avons constaté que la méthode extend() peut déjà fonctionner normalement. Mais il y a un problème. Ce qui est donné ci-dessus est ce qu'on appelle un clone superficiel. Lors de l'utilisation d'une copie superficielle, si les propriétés de l'objet enfant sont modifiées et que la propriété s'avère être un objet, cette opération modifiera également l'objet parent. Dans de nombreux cas, ce n'est pas le résultat souhaité. Considérez ce qui suit :

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extend(dad) //调用extend()方法将dad的属性复制到kid上面
kid.counts.push(4); //把4追加到kid.counts数组里面
console.log(dad.counts); //[1, 2, 3, 4] 

A travers l'exemple ci-dessus, nous constaterons qu'après avoir modifié l'attribut kid.counts (en y ajoutant l'élément 4), dad.counts sera également affecté. En effet, lors de l'utilisation d'une copie superficielle, puisque l'objet est passé par référence, c'est-à-dire que kid.counts et dad.counts pointent vers le même tableau (ou en mémoire, ils pointent vers la même adresse de tas).

Ensuite, modifions la fonction extend() pour implémenter la copie profonde. Ce que nous devons faire est de vérifier chaque attribut de l'objet parent, et si l'attribut s'avère être un objet, puis de copier récursivement les attributs de l'objet. De plus, vous devez également détecter si l'objet est un tableau. En effet, la méthode de création littérale des tableaux est différente de la méthode de création littérale des objets. La première est [] et la seconde est {}. Pour détecter un tableau, vous pouvez utiliser la méthode Object.prototype.toString(). S'il s'agit d'un tableau, il renverra "[object Array]". Jetons un coup d'œil à la fonction extend() de la version deep copy :

function extendDeep(parent, child) {
child = child || {};
for(var i in parent) {
if(parent.hasOwnProperty(i)) {
//检测当前属性是否为对象
if(typeof parent[i] === "object") {
//如果当前属性为对象,还要检测它是否为数组
//这是因为数组的字面量表示和对象的字面量表示不同
//前者是[],而后者是{}
child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {};
//递归调用extend
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
} 

D'accord, la fonction de copie profonde a été écrite. Testons-la pour voir si elle peut fonctionner comme prévu, c'est-à-dire si la copie profonde peut être réalisée :

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
//修改kid的counts属性和reads属性
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

Grâce à l'exemple ci-dessus, nous pouvons constater que même si les kid.counts et kid.reads de l'objet enfant sont modifiés, les dad.counts et kid.reads de l'objet parent n'ont pas changé, notre objectif a donc été atteint.

Ce qui suit est un résumé des idées de base pour la mise en œuvre de la copie approfondie :

1. Vérifiez si l'attribut actuel est un objet

2. Les tableaux étant des objets spéciaux, il est nécessaire de détecter si l'attribut est un tableau s'il s'agit d'un objet.

3. S'il s'agit d'un tableau, créez un tableau vide [], sinon, créez un objet vide {} et attribuez-le à la propriété actuelle de l'objet enfant. Ensuite, la fonction extendDeep est appelée de manière récursive.

L'exemple ci-dessus utilise une méthode de copie profonde implémentée par nous-mêmes à l'aide d'un algorithme récursif. En fait, les deux méthodes fournies par le nouvel objet JSON dans ES5 peuvent également réaliser une copie profonde, à savoir JSON.stringify() et JSON.parse() ; la première est utilisée pour convertir l'objet en chaîne, et la seconde est utilisée ; pour convertir la chaîne Convertir en objet. Ci-dessous, nous utilisons cette méthode pour implémenter une fonction de copie approfondie :

function extendDeep(parent, child) {
var i,
proxy;
proxy = JSON.stringify(parent); //把parent对象转换成字符串
proxy = JSON.parse(proxy) //把字符串转换成对象,这是parent的一个副本
child = child || {};
for(i in proxy) {
if(proxy.hasOwnProperty(i)) {
child[i] = proxy[i];
}
}
proxy = null; //因为proxy是中间对象,可以将它回收掉
return child;
} 

Ce qui suit est un exemple de test :

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
//修改kid的counts属性和reads属性
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

Le test a révélé qu'il permettait également une copie approfondie. Il est généralement recommandé d'utiliser cette dernière méthode, car JSON.parse et JSON.stringify sont des fonctions intégrées et seront traitées plus rapidement. De plus, la méthode précédente utilise des appels récursifs. Nous savons tous que la récursion est un algorithme relativement inefficace.

Il s'agit de la méthode d'implémentation du clone profond JavaScript. J'espère que cela vous sera utile !

Déclaration:
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