Maison >Problème commun >Quelles sont les trois méthodes de mise en œuvre de la copie profonde ?
Les trois méthodes d'implémentation de la copie profonde sont : 1. Copier récursivement tous les attributs hiérarchiques ; 2. Implémenter en utilisant l'analyse et la stringification des objets JSON ; 3. Emprunter la méthode d'extension de JQ ;
Les trois méthodes d'implémentation de la copie profonde sont :
1, copier récursivement tous les attributs hiérarchiques
function deepClone(obj){ let objClone = Array.isArray(obj)?[]:{}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判断ojb子元素是否为对象,如果是,递归复制 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ //如果不是,简单复制 objClone[key] = obj[key]; } } } } return objClone; } let a=[1,2,3,4], b=deepClone(a); a[0]=2; console.log(a,b);
Comme imaginé auparavant, b est désormais hors du contrôle de a et n'est plus affecté par a.
J'insiste encore une fois sur le fait que la copie profonde consiste à copier les attributs de chaque niveau de l'objet. Vous pouvez voir un exemple. Il existe une méthode d'extension dans JQ qui peut également copier des objets. Voyons si
let a=[1,2,3,4], b=a.slice(); a[0]=2; console.log(a,b);
cela signifie-t-il que la méthode slice est également une copie complète après tout, b ? non affecté par a. , comme mentionné ci-dessus, la copie profonde copiera-t-elle les attributs de tous les niveaux ? Dans cet exemple, nous changeons a en
2. En plus de la récursivité, nous pouvons également emprunter l'analyse et la stringification d'objets JSON
let a=[0,1,[2,3],4], b=a.slice(); a[0]=1; a[2][0]=1; console.log(a,b);
3 En plus des deux méthodes ci-dessus, nous pouvons également emprunter la méthode d'extension de JQ.
$.extend( [deep ], target, object1 [, objectN ] )
deep indique s'il faut copier profondément. Si c'est vrai, c'est une copie profonde. Si c'est faux, c'est une copie superficielle.
object1 objectN
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4],
b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);
Vous pouvez voir que l'effet est le même que la méthode ci-dessus, mais vous devez vous fier à la bibliothèque JQ.
4._.cloneDeep() de lodash
Ce qui suit est une solution au problème de copie approfondie que j'ai lu. JSON.parse
Convertissez d'abord un objet en objet json. Analysez ensuite cet objet json.
let a=[0,1,[2,3],4], b=$.extend(true,[],a); a[0]=1; a[2][0]=1; console.log(a,b);
let obj = {a:{b:22}};let copy = JSON.parse(JSON.stringify(obj));telle que Map, Set, RegExp, Date, ArrayBuffer et d'autres éléments intégrés Les types sont perdus lors de la sérialisation.
let a = {};let b = {a};a.b = b;let copy = JSON.parse(JSON.stringify(a));La valeur de a est imprimée comme suit
copy的值打印如下
对比发现,Set已丢失。
MessageChannel
建立两个端,一个端发送消息,另一个端接收消息。
function structuralClone(obj) { return new Promise(resolve =>{ const {port1, port2} = new MessageChannel(); port2.onmessage = ev => resolve(ev.data); port1.postMessage(obj); }) } const obj = /* ... */; structuralClone(obj).then(res=>{ console.log(res); })
这种方法的优点就是能解决循环引用的问题,还支持大量的内置数据类型。缺点就是这个方法是异步的。
History API
利用history.replaceState。这个api在做单页面应用的路由时可以做无刷新的改变url。这个对象使用结构化克隆,而且是同步的。但是我们需要注意,在单页面中不要把原有的路由逻辑搞乱了。所以我们在克隆完一个对象的时候,要恢复路由的原状。
function structuralClone(obj) { const oldState = history.state; history.replaceState(obj, document.title); const copy = history.state; history.replaceState(oldState, document.title); return copy; }var obj = {};var b = {obj}; obj.b = bvar copy = structuralClone(obj); console.log(copy);
这个方法的优点是。能解决循环对象的问题,也支持许多内置类型的克隆。并且是同步的。但是缺点就是有的浏览器对调用频率有限制。比如Safari 30 秒内只允许调用 100 次
Notification API
这个api主要是用于桌面通知的。如果你使用Facebook的时候,你肯定会发现时常在浏览器的右下角有一个弹窗,对就是这家伙。我们也可以利用这个api实现js对象的深拷贝。
function structuralClone(obj) { return new Notification('', {data: obj, silent: true}).data; }var obj = {};var b = {obj}; obj.b = bvar copy = structuralClone(obj); console.log(copy)
同样是优点和缺点并存,优点就是可以解决循环对象问题,也支持许多内置类型的克隆,并且是同步的。缺点就是这个需要api的使用需要向用户请求权限,但是用在这里克隆数据的时候,不经用户授权也可以使用。在http协议的情况下会提示你再https的场景下使用。
lodash的_.cloneDeep()
支持循环对象,和大量的内置类型,对很多细节都处理的比较不错。推荐使用。
支持的类型有很多
我们这里再次关注一下lodash是如何解决循环应用这个问题的?
从相关的代码中。我们可以发现。lodash是用一个栈记录了。所有被拷贝的引用值。如果再次碰到同样的引用值的时候,不会再去拷贝一遍。而是利用之前已经拷贝好的值。
我们仅仅实现一个简易点的深拷贝。能优雅的处理循环引用的即可。在实现深拷贝之前,我们首先温习回顾一下js中的遍历对象的属性的方法和各种方法的优缺点。
js中遍历一个对象的属性的方法
实现深拷贝,解决循环引用问题
/** * 判断是否是基本数据类型 * @param value */function isPrimitive(value){ return (typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol' || typeof value === 'boolean') }/** * 判断是否是一个js对象 * @param value */function isObject(value){ return Object.prototype.toString.call(value) === "[object Object]"}/** * 深拷贝一个值 * @param value */function cloneDeep(value){ // 记录被拷贝的值,避免循环引用的出现 let memo = {}; function baseClone(value){ let res; // 如果是基本数据类型,则直接返回 if(isPrimitive(value)){ return value; // 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值 }else if(Array.isArray(value)){ res = [...value]; }else if(isObject(value)){ res = {...value}; } // 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝 Reflect.ownKeys(res).forEach(key=>{ if(typeof res[key] === "object" && res[key]!== null){ //此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题 if(memo[res[key]]){ res[key] = memo[res[key]]; }else{ memo[res[key]] = res[key]; res[key] = baseClone(res[key]) } } }) return res; } return baseClone(value) }
验证我们写的cloneDeep是否能解决循环应用的问题
var obj = {};var b = {obj}; obj.b = bvar copy = cloneDeep(obj); console.log(copy);
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!