这篇文章主要讲解了“怎么解决JavaScript的深浅拷贝”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么解决JavaScript的深浅拷贝”吧!
正文
从一则故事讲起,昨天因为医院开不出药,我拿上药取小区药店去买药,进门之后我问老板有没有这个药,老板转身进去一个小屋子拿了一盒药,果不其然确实有,药的名字和毫克一摸一样,但是盒子的样子和厂商不一样,我问老板:“这两个药是一种药吗,盒子不一样啊,药的成分是一样的吗?”老板说当然一样啊,这个就和你去买猪肉一样,同样是猪身上的肉,只不过是你去这个超市和去其他超市买场地一样而已。最后为了安全起见,我还是没有买那个药。
"拷贝"分为浅拷贝和深拷贝。它是针对对象
来说的,如果不是对象一切免谈。这里的对象可以理解为我拿的那盒药,浅拷贝
可以理解为老板拿出来的那盒药,虽然药的名字和毫克一样,然后里面的我们不知道是否真的一样,可能一样可能不一样。深拷贝
可以理解为我买到了一摸一样的药,一层一层的药名,毫克,厂商,成分都一样。
总结:
浅拷贝就是针对对象的属性依次进行复制,只复制一层,不会递归到个属性复制,会产生
引用
问题即内存地址是指的同一地址。简单来说就是拷贝之后和原对象有关
。深拷贝就是针对对象的各属性递归复制到新的对象上,内存地址不会指向同一地址。简单来说就是拷贝之后和元对象
无关
。
下面看一个浅拷贝的例子:
let school={'name':"W3Cschool"};let my = {age:{count:18},name:"W3Cschool编程网"};let all = {...school,...my};my.age.count=100;console.log(all);console.log(my);
结果:
{ age: { count: 100 }, name: 'W3Cschool编程网' }{ age: { count: 100 }, name: 'W3Cschool编程网' }
结论是:浅拷贝修改拷贝之后的对象上的属性会把原对象身上的属性同时修改掉。
下面再看一个深拷贝的例子:
const _ = require("loadsh")let my = {age:{count:18},name:"W3Cschool编程网"};let all = _.cloneDeep(my);all.age.count =100;console.log(my);console.log(all);
结果:
{ age: { count: 18 }, name: 'W3Cschool编程网' }{ age: { count: 100 }, name: 'W3Cschool编程网' }
结论是:深拷贝修改拷贝之后的对象上的属性不会把原对象身上的属性同时修改掉。
拷贝的方法
数组方法:slice和concat
slice
let arr = [1,2,3,4];let arr2 = arr.slice(0)arr2[2]=5;console.log(arr); //[ 1, 2, 3, 4 ]console.log(arr2); //[ 1, 2, 5, 4 ]
当数组里是不是对象的时候从结果上看是深拷贝,在看下面例子
let arr = [{1:1,2:2}];let arr2 = arr.slice(0)arr2[2]=5;console.log(arr); //[ { '1': 1 }, { '2': 5 } ]console.log(arr2); //[ { '1': 1 }, { '2': 5 } ]
当数组里是对象的时候就变成了浅拷贝
concat
let arr = [1,2,3,4];let arr2 = [].concat(arr);arr2[2]=5;console.log(arr); //[ 1, 2, 3, 4 ] ✔console.log(arr2); //[ 1, 2, 5, 4 ]
当数组里不是对象的时候从结果上看是深拷贝,在看下面例子
let arr = [{1:1},{2:2}];let arr2 = arr.cancat(0)arr2[1][2]=5;console.log(arr); //[ { '1': 1 }, { '2': 5 } ] ❌变成了引用console.log(arr2); //[ { '1': 1 }, { '2': 5 } ]
当数组里是对象的时候就变成了浅拷贝
总结:只有当数组是一维数组而且不包含对象的时候才是深拷贝
(推荐教程:JavaScript教程)
Object.assgin()
let a= {a:1,b:2};let b= Object.assign({},a);a.a=3;console.log(a) //{a: 3, b: 2}console.log(b) //{a: 1, b: 2} ✔let a= {a:1,b:{c:2}};let b= Object.assign({},a);a.b.c=3;console.log(a) //{a: 1, b: {c:3}}console.log(b) //{a: 1, b: {c:3}} ❌变成了引用
总结:Object.assgin如果涉及到嵌套多个对象的话就变成了引用 解决方法:使用JSON.stringify()
先转化成字符串,再通过JSON.parse()
转化成对象
3.JSON.parse(JSON.stringify())
let a= {a:1,b:{c:2}};let b= JSON.parse(JSON.stringify(a))a.b.c=3;console.log(a) //{a: 1, b: {c:3}}console.log(b) //{a: 1, b: {c:2}} ✔let school={'name':"W3Cschool编程网",fn:function(){}};let my = {age:{count:18},name:"W3Cschool编程网"};let all=JSON.parse(JSON.stringify({...school,...my}))console.log(all); //{'name':"W3Cschool编程网",age:{count:18}}; //❌把fn给丢了
总结: JSON.parse(JSON.stringify()) 这个方法有一定的局限性,会丢失 fn 。
手写深拷贝
let deepClone=(obj)=>{ if(obj==undefined) return obj; //undefined == null if(obj instanceof RegExp) return new RegExp(obj); if(obj instanceof Date) return new Date(obj); if(typeof obj!=="object") return obj; let newObj = new obj.constructor; for(let key in obj){ if(obj.hasOwnProperty(key)){ newObj[key] = deepClone(obj[key]) } } return newObj;}let obj1 = {name:{age:"10"}}let n = deepClone(obj1)obj1.name.age = "231"console.log(n); //{name:{age:"10"}} ✔let obj = { name:"W3Cschool编程网" }obj.aaa=objlet n = deepClone(obj1)console.log(n); //死循环了 ❌
解决这个问题可以使用WeakMap
let deepClone=(obj,hash=new WeakMap())=>{ if(obj==undefined) return obj; //undefined == null if(obj instanceof RegExp) return new RegExp(obj); if(obj instanceof Date) return new Date(obj); if(typeof obj!=="object") return obj; if(hash.has(obj)) return hash.get(obj); let newObj = new obj.constructor; hash.set(obj,newObj); for(let key in obj){ if(obj.hasOwnProperty(key)){ newObj[key] = deepClone(obj[key],hash) } } return newObj;}
lodash的cloneDeep
<br> 源码地址:https://github.com/lodash/lodash/blob/86a852fe763935bb64c12589df5391fd7d3bb14d/.internal/baseClone.js<br> ```
vue-router源码中的克隆方法
function clone (value) { if (Array.isArray(value)) { return value.map(clone) } else if (value && typeof value === 'object') { const res = {} for (const key in value) { res[key] = clone(value[key]) } return res } else { return value }}let arr = [{1:1},{2:2},function(){}];let arr2 = clone(arr)arr2[1][2]=5;console.log(arr) //[ { '1': 1 }, { '2': 2 }, [Function (anonymous)] ] ✔ 深拷贝console.log(arr2); //[ { '1': 1 }, { '2': 5 }, [Function (anonymous)] ]function extend (a, b) { for (const key in b) { a[key] = b[key] } return a}let b={a:1,b:{c:2}};let a= extend({},b);a.b.c=5;console.log(a); //{ a: 1, b: { c: 5 } }console.log(b); //{ a: 1, b: { c: 5 } } 浅拷贝
感谢各位的阅读,以上就是“怎么解决JavaScript的深浅拷贝”的内容了,经过本文的学习后,相信大家对怎么解决JavaScript的深浅拷贝这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!