资讯专栏INFORMATION COLUMN

9012年,当我们讨论js深浅拷贝时我们在说些什么?

xeblog / 3388人阅读

摘要:正文讨论深浅拷贝,首先要从的基本数据类型说起根据中的变量类型传递方式,分为值类型和引用类型,值类型变量包括。当你拷贝的对象有多级的时候,就是深拷贝。数据不存在则对其拷贝。

前言:

本文主要阅读对象:对深浅拷贝印象模糊对初级前端,想对js深浅拷贝聊一聊的中级前端。 如果是对这些有完整对认知体系和解决方法的大佬,可以选择略过。

正文: 讨论深浅拷贝,首先要从js的基本数据类型说起: 根据 JavaScript 中的变量类型传递方式,分为值类型和引用类型, 值类型变量包括 Boolean、String、Number、Undefined、Null。
引用类型包括了 Object 类的所有, 如 Date、Array、Function 等。在参数传递方式上,值类型是按值传递,引用类型是按地址传递。

我们来看一下这两则有什么区别:

//eg1: // 值类型 var a = 10 var b = a b = 20 console.log(a) // 10 console.log(b) // 20 //解析:上述代码中,a b都是值类型,两者分别修改赋值,相互之间没有任何影响。再看引用类型的例子: //eg2 // 引用类型 var a = {x: 10, y: 20} var b = a b.x = 100 b.y = 200 console.log(a) // {x: 100, y: 200} console.log(b) // {x: 100, y: 200}

解析: 上述代码中,a b都是引用类型。在执行了b = a之后,修改b的属性值,a的也跟着变化。因为a和b都是引用类型,指向了同一个内存地址,即两者引用的是同一个值,因此b修改属性时,a的值随之改动。

**那到底什么是深浅拷贝呢?**
解析:深浅拷贝是拷贝对象的`深度`来说的:
当你想拷贝的对象只有一级时,就是浅拷贝。
当你拷贝的对象有多级的时候,就是深拷贝。

再回到上面的那个例子: 在例子能看出来,如果直接采用“=”赋值,这种类型,当我们改变b的值时候,a也会随之改变的,假如不允许改变a呢? 这时,深拷贝就出场了。

function clone(val){ if(!val && typeof val !== "object"){ return } const newArr = toString.call(val) === ["object Array"] ? [] : {} for (let item in val) { if(typeof val[item] === "object") { newArr[item] = clone(item) }else { newArr[item] = val[item] } } return newArr } //测试: var a = {x: 10, y: 20} var b = clone(a) b.x = 100 b.y = 200 console.log(a) // {x: 10, y: 20} console.log(b) //{x: 100, y: 200}

解析: 对于这个深拷贝,大家看到这里应该都可以看明白:主要思路就是浅拷贝 + 递归。
这有几个点需要注意:
1. 判断接收到的参数类型。
2. 使用toString.call()判断更加严谨。
3. 考虑对其他引用类型的数据兼容(这里并没有对每种类型的数据组做判断,引用类型包括了 Object 类的所有,如 Date、Array、Function 等。)

需要注意的是:用递归的话,会有一下几个需要特别注意的点: 1. 当数据层次很深时,容易爆栈(栈溢出) 解决办法=> a. 消除尾递归 b. 改用循环 2. “循环引用”,会导致死循环 解决办法 => a. 循环检测 b. 暴力破解 第一种:数据广度特别大,层次特别深 第二种:类似下面的这种情况 var a = {} a.a = a clone(a) //死循环 ES6中一行代码实现深拷贝:JSON.parse(JSON.stringify(source)) 注释:JSON.stringify()其实利用了“循环检测”机制

这里给大家推荐一篇循环解决递归的方法: 可以保持拷贝数据以后的引用关系

function cloneForce(x) { const uniqueList = []; // 用来去重 let root = {}; // 循环数组 const loopList = [ { parent: root, key: undefined, data: x, } ]; while(loopList.length) { // 深度优先 const node = loopList.pop(); const parent = node.parent; const key = node.key; const data = node.data; // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素 let res = parent; if (typeof key !== "undefined") { res = parent[key] = {}; } // 数据已经存在 let uniqueData = find(uniqueList, data); if (uniqueData) { parent[key] = uniqueData.target; continue; // 中断本次循环 } // 数据不存在 // 保存源数据,在拷贝数据中对应的引用 uniqueList.push({ source: data, target: res, }); for(let k in data) { if (data.hasOwnProperty(k)) { if (typeof data[k] === "object") { // 下一次循环 loopList.push({ parent: res, key: k, data: data[k], }); } else { res[k] = data[k]; } } } } return root; } function find(arr, item) { for(let i = 0; i < arr.length; i++) { if (arr[i].source === item) { return arr[i]; } } return null; } //eg1: var a = { a1: b, a2: b, } var b = {}; a.a1 === a.a2 // true var c = cloneForce(a); c.a1 === c.a2 // true 引用保持一致 //eg2: var a = {}; a.a = a; console.log(cloneForce(a))//还可以破解循环引用

主要思路是: 声明一个数组对象(父对象,key,value),其有值时,取其最后一个对象; 判断key有值,代表当前级下有子级,则拷贝到子元素。如果数据已经存在,则中断本次循环。数据不存在则对其拷贝。
有一点需要注意:cloneForce在对象数量很多时会出现很大的问题,如果数据量很大不适合使用cloneForce。

有兴趣的同学,可以前往 深拷贝的终极探索(90%的人都不知道),查看更多的细节。

总结:
如果觉得对你有帮助,请给作者一点小小的鼓励, 点个赞或者收藏吧。 有需要沟通的请联系我: 微信( wx9456d )邮箱( allan_liu986@163.com )

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/6669.html

相关文章

  • 一篇文章彻底说清JS的深拷贝/浅拷贝

    摘要:一篇文章彻底说清的深拷贝浅拷贝这篇文章的受众第一类业务需要急需知道如何深拷贝对象的开发者。这篇文章分享的目的更多还是希望用一篇文章整理清楚深浅拷贝的含义递归实现思路以及小伙伴们如果使用了这种黑科技一定要清楚这样写的优缺点。 一篇文章彻底说清JS的深拷贝and浅拷贝 这篇文章的受众 第一类,业务需要,急需知道如何深拷贝JS对象的开发者。 第二类,希望扎实JS基础,将来好去面试官前秀操作...

    J4ck_Chan 评论0 收藏0
  • 一篇文章彻底说清JS的深拷贝/浅拷贝

    摘要:一篇文章彻底说清的深拷贝浅拷贝这篇文章的受众第一类业务需要急需知道如何深拷贝对象的开发者。这篇文章分享的目的更多还是希望用一篇文章整理清楚深浅拷贝的含义递归实现思路以及小伙伴们如果使用了这种黑科技一定要清楚这样写的优缺点。 一篇文章彻底说清JS的深拷贝and浅拷贝 这篇文章的受众 第一类,业务需要,急需知道如何深拷贝JS对象的开发者。 第二类,希望扎实JS基础,将来好去面试官前秀操作...

    lakeside 评论0 收藏0
  • 一篇文章彻底说清JS的深拷贝/浅拷贝

    摘要:一篇文章彻底说清的深拷贝浅拷贝这篇文章的受众第一类业务需要急需知道如何深拷贝对象的开发者。这篇文章分享的目的更多还是希望用一篇文章整理清楚深浅拷贝的含义递归实现思路以及小伙伴们如果使用了这种黑科技一定要清楚这样写的优缺点。 一篇文章彻底说清JS的深拷贝and浅拷贝 这篇文章的受众 第一类,业务需要,急需知道如何深拷贝JS对象的开发者。 第二类,希望扎实JS基础,将来好去面试官前秀操作...

    big_cat 评论0 收藏0
  • 深入理解JS深浅拷贝

    摘要:深拷贝相比于浅拷贝速度较慢并且花销较大。所以在赋值完成后,在栈内存就有两个指针指向堆内存同一个数据。结果如下扩展运算符只能对一层进行深拷贝如果拷贝的层数超过了一层的话,那么就会进行浅拷贝那么我们可以看到和展开原算符对于深浅拷贝的结果是一样。 JS中数据类型 基本数据类型: undefined、null、Boolean、Number、String和Symbol(ES6) 引用数据类型:...

    JackJiang 评论0 收藏0
  • JavaScript深入浅出

    摘要:理解的函数基础要搞好深入浅出原型使用原型模型,虽然这经常被当作缺点提及,但是只要善于运用,其实基于原型的继承模型比传统的类继承还要强大。中文指南基本操作指南二继续熟悉的几对方法,包括,,。商业转载请联系作者获得授权,非商业转载请注明出处。 怎样使用 this 因为本人属于伪前端,因此文中只看懂了 8 成左右,希望能够给大家带来帮助....(据说是阿里的前端妹子写的) this 的值到底...

    blair 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<