写点什么

【JS】大白话 - 深拷贝与浅拷贝 - 及其原生 JS 实现方式

作者:Sam9029
  • 2022 年 9 月 09 日
    四川
  • 本文字数:2574 字

    阅读完需:约 8 分钟

【JS】大白话-深拷贝与浅拷贝-及其原生JS实现方式

🦖我是 Sam9029,一个前端

Sam9029 的 InfoQ 主页:Sam9029 (infoq.cn)

**🐱‍🐉🐱‍🐉恭喜你,若此文你认为写的不错,不要吝啬你的赞扬,求收藏,求评论,求一个大大的赞!👍**

⭐⭐深拷贝与浅拷贝

(仅谈论了 基本数据类型 和 ES5 的引用数据类型 的深拷贝与浅拷贝)

(关于 ES6 及之后 新增的 Symbol,Map 等数据类型还有待谈论)

(以下仅为 原生 JS 的方法,未涉及第三方库使用,第三方库lodash也有函数可实现)


❗- 因文字字数原因,涉及的 ⭐对 函数对象 的拷贝(面试常考) 在另一文章中阐述,文章末尾附带地址

注意概念上的区别(很重要):

  • 浅拷贝与 深拷贝 都是需要开辟新内存地址,产生新对象

  • 但是

  • ⭐浅拷贝 只拷贝 原始对象中的 最外部一层

  • ⭐深拷贝 则会拷贝 原始对象中 所有的层级


拓展:深浅拷贝与赋值的区别

  • ❗特别 浅拷贝 不是 赋值操作 ,这俩就不是一个概念

  • 非要整个好活记,深浅拷贝这俩才是 一丘之貉,更本瞧不上赋值

  • 深浅拷贝 主要针对 引用数据类型

  • 赋值 主要是 用于 基本数据类型

  • 当然也可以用于引用数据类型,但此时就要考虑新旧对象的同一引用地址问题了

⭐示例:

对于下面这个 对象


const hotGirl = {    name: 'Maji',    age: 28,    info: {        desc: 'superHot',    },};
复制代码


  • 🦊浅拷贝:

  • 只能拷贝属性nameage拥有新地址,但是 拷贝到 info 时 则会引用原始对象 中的地址

  • (!!!即改变原始对象中的info.desc的值时,浅拷贝出来的新对象的info.desc的值同样会改变),

  • 属于不彻底拷贝,所以 只有一层属性的对象 适合浅拷贝。

  • 🦊深拷贝

  • 则可以 完全 拷贝所有层级,变成一个拥有 新地址 的 新对象。属于彻底的拷贝

  • 改变原始对象的 info.desc 该值也不会影响新对象的info.desc的值



浅拷贝:只拷贝最外面一层的数据-(不彻底的拷贝)

一 使用Object.assign

  • 语法:`Object.assign(target,source01,source02,...)`

  • (将source对象中的值 拷贝到 target目标对象中)

  • target 可以写成空对象

  • 注意:若 target 非空 ,且 source 和 target 中有重复属性 ,则 target 中的属性会被覆写;target 的值会被改变, 但是 source 的值不会

示例: (得到新的对象)

const hotGirl = {    name: 'Maji',    age: 28,};// target `可以写成空对象`let newHotGirl = Object.assign({},hotGirl)console.log(newHotGirl)// {name: 'Maji', age: 28}
复制代码


示例 (重新赋值给目标对象)

const hotGirl = {    name: 'Maji',    age: 28,};const pureGirl = {    name: 'Olla',    age: 26,    tall:"180cm"};
// 让Olla 从 pureGirl 成为 hotGirl // 注意这里 hotGirl 的属性被改变了,pureGirl没有Object.assign(hotGirl,pureGirl)
// target `可以写成空对象`console.log(hotGirl)// {name: 'Olla', age: 26, tall: '180cm'}
复制代码


二 使用扩展语法 (...)

  • 使用简单,不必多说


const hotGirl = {    name: 'Maji',    age: 28,};let newHotGirl = { ...hotGirl }console.log(newHotGirl)//{name: 'Maji', age: 28}
复制代码

三 for..in 循环拷贝

const hotGirl = {    name: 'Maji',    age: 28,};
const newHotGirl = {};// 用 for in 将 obj1 的值拷贝给 obj2for (let key in hotGirl) { //字符串 赋值 就是开辟 新的内存地址,成为完全的新值 newHotGirl[key] = hotGirl[key];}console.log(newHotGirl)//{name: 'Maji', age: 28}
复制代码



深拷贝: 每一层数据都会拷贝-(最彻底的拷贝)

一 JSON 方法

  • 语法:let newObj = JSON.parse(JSON.stringify(obj))


缺陷(暂不谈论,死记,❗重要性高些)

  • ❗会忽略 undefined

  • 会忽略 symbol

  • ❗会忽略函数:JSON.stringify() 会默认移除函数。

  • JSON.stringify() 无法拷贝 Map、Set、RegExp 这些特殊数据类型。

  • 不能序列化函数 ???

  • 不能解决循环引用的对象 见[前端进阶之道-深浅拷贝](JS | 前端进阶之道 (yuchengkai.cn))

二 ⭐⭐ JS 有原生的深拷贝 API structuredClone

MDN---structuredClone() - Web 开发技术 | MDN (mozilla.org)](https://developer.mozilla.org/zh-CN/docs/web/api/structuredClone))


  • 结构化拷贝算法的实现,能够实现几乎对所有数据类型的深拷贝

  • 该 API 较新,兼容要考虑(目前主流都支持) ,虽然有些缺点,但是不影响,放心用,以后会加大支持力度

  • 可引入 core-js 兼容库 core-js#structuredclone,兼容旧版本。

  • 相比 JSON.parse()structuredClone API 的性能更好

  • 语法:

缺点:

  • (死记,放心用,以后会加大支持力度,❗重要性高些)

  • 原型:无法拷贝对象的原型链。

  • ❗函数:无法拷贝函数。(普通函数、箭头函数、类、方法)


  • ❗DOM 节点

  • 不可克隆:并没有支持所有类型的拷贝,比如 Error

三 深拷贝 for ... in 手写

 function deepCopy(obj) {     //判断 传入对象 为 数组 或者 对象     var result = Array.isArray(obj) ? [] : {};     // for in 遍历     for (var key in obj) {         // 判断 是否 为自身 的属性值(排除原型链干扰)         if (obj.hasOwnProperty(key)) {             // 判断 对象的属性值 中 存储的 数据类型 是否为对象             if (typeof obj[key] === 'object') {                 // 递归调用                 result[key] = deepCopy(obj[key]);   //递归复制             }              // 不是的话 直接 赋值 copy             else {                 result[key] = obj[key];             }         }     }     // 返回 新的对象     return result; }
复制代码


优点:(相对的)

  • 可以实现 函数 拷贝

  • 注意其中有些许问题,及函数对象属性并未实现开辟新地址作为新对象,而是引用了原本对象的引用地址来调用

  • 若想要继续了解,请往下看拓展

  • 在深拷贝的前两种实现方式上(JSON,InstructuredClone)都无法对 函数 进行深拷贝,而此种手写的 for-in 方法可以


拓展--对 函数 的 拷贝

在 JS 中 函数 也是一个对象,所以是通过引用地址来调用的

链接🔗:(待写)


参考文章

30-浅拷贝和深拷贝 | 千古前端图文教程 (qianguyihao.com)


JS | 前端进阶之道 (yuchengkai.cn)


JS中对象深拷贝:structuredClone()_俊刚的博客-CSDN博客


JS 深拷贝的原生终结者 structuredClone API - 掘金 (juejin.cn)



🦖我是 Sam9029,一个前端,坚信应无所往

文章若有错误,敬请指正🙏

**🐱‍🐉🐱‍🐉恭喜你,都看到这了,求收藏,求评论,求一个大大的赞👍!不过分吧**

Sam9029 的 InfoQ 主页:Sam9029 (infoq.cn)


发布于: 刚刚阅读数: 3
用户头像

Sam9029

关注

庸俗 2022.09.03 加入

好色

评论

发布
暂无评论
【JS】大白话-深拷贝与浅拷贝-及其原生JS实现方式_JavaScript_Sam9029_InfoQ写作社区