首页 > 其他分享 >关于JS中深拷贝和浅拷贝的思考

关于JS中深拷贝和浅拷贝的思考

时间:2022-12-29 22:56:55浏览次数:55  
标签:obj 对象 JS 中深 引用 测试 拷贝 数据

概要:对Js数据的深拷贝和浅拷贝做一个总结,加深记忆

Js数据类型

由基本数据类型和引用数据类型组成,简单数据类型包括(Number、String、Boolean、Undefined、Null);引用数据类型包括(Object、Array、Function)。两者的数据结构和存储方式都有所不同。具体参考我写的另一篇博文(Blog:js数据在内存中的存储方式-2021.04.29)。

由于不同的数据存储方式,Js引用数据类型拷贝具有深拷贝和浅拷贝的区别。

Js数据浅拷贝

顾名思义,比较浅层面的拷贝。对于引用数据来说,一个变量保存了一个引用类型数据,那么该变量其实是保存了一个指向数据堆中该引用数据的一个引用地址(理解为“变量保存了一个指向数据堆中引用类型数据的指针”可能对一些小伙伴来说更好理解)。所以在浅层面变量保存的是引用地址(指针),浅拷贝就是拷贝引用地址(指针)。代码示例。

const obj = {
	name: '测试对象'
}
//对象是一个引用类型数据,obj这个变量保存了指向{name:‘测试对象’}(方便理解这么写)这个对象的引用地址,这个引用地址是该数据在数据堆中位置,比如我拿二维坐标模拟,现在存的是(x=1米,y=2米)。
const obj2 = obj;
//实现浅拷贝
//obj2将obj保存的(x=1米,y=2米)这么一个坐标位置复制过来,赋值到自己身上。
//那么obj2对应的数据跟上面obj一样,是同一个{name: '测试对象'},因为坐标是一样的嘛。

实现浅拷贝的方法

1、变量赋值

Js数据深拷贝

顾名思义,实现深层次的拷贝,深挖到数据堆中,将对应数据堆中的数据进行拷贝一份而不是拷贝引用地址。

如下代码示例。

//为方便解释,暂时对象中只包含基本类型数据的属性。
const obj = {
	name: '测试对象',
    age: 12
}

const obj1 = {} //初始化一个空对象,拿来拷贝obj

for(let key in obj) {
	obj1[key] = obj[key]
}
//以下是测试
//使用node命令运行js文件
console.log(obj)//{ name: '测试对象', age: 12 }
console.log(obj1)//{ name: '测试对象', age: 12 }
//两者内容一样,实现了拷贝
console.log(obj === obj1)//false
//两者保存的不是同一个数据,实现了深拷贝

❗但是在实际中对象属性不一定是基本类型数据,也有可能是引用类型数据,所以需要使用其他方法实现深拷贝。

实现深拷贝的方法

1、递归
function deepClone(source) {
  const newObj = source.constructor === Array ? [] : {};//判断source(被拷贝目标)是数组还是对象
  for(let key in source) {
    if(source.hasOwnProperty(key)) {
      if(source[key] && typeof source[key] === 'object'){
        //判断每一次遍历属性的值是否存在并且是对象,则递归
        newObj[key] = source.constructor === Array ? [] : {};
        newObj[key] = deepClone(source[key]);
      }
      else {
        newObj[key] = source[key];
      }
    }
  }
  return newObj;//返回拷贝处理后的对象,以用于递归,递归完成后该对象完成深拷贝
}
//自定义一个含有许多引用类型数据属性的对象
const obj = {
  name: '测试对象2',
  list: [1,2,3,4,5],
  post: {
    title: '测试标题',
    content: '测试内容'
  },
  music: [
    {
      mtitle: '晴天',
      mauthor: '周杰伦'
    },
    {
      mtitle: '美人鱼',
      mauthor: '林俊杰'
    }
  ]
}
const obj1 = deepClone(obj);//调用写好的拷贝方法,将obj拷贝给obj1
//以下是测试
console.log(obj);
/*
{
  name: '测试对象2',
  list: [ 1, 2, 3, 4, 5 ],
  post: { title: '测试标题', content: '测试内容' },
  music: [
    { mtitle: '晴天', mauthor: '周杰伦' },
    { mtitle: '美人鱼', mauthor: '林俊杰' }
  ]
}
*/
console.log(obj1);
/*
{
  name: '测试对象2',
  list: [ 1, 2, 3, 4, 5 ],
  post: { title: '测试标题', content: '测试内容' },
  music: [
    { mtitle: '晴天', mauthor: '周杰伦' },
    { mtitle: '美人鱼', mauthor: '林俊杰' }
  ]
}
*/
//两者内容一样,实现了拷贝
console.log(obj === obj1);//false
//两者不是引用的同一个对象,实现了深拷贝
2、使用JSON.stringify和JSON.parse

总所周知,json是一种字符串形式的数据,js中使用JSON方法将json数据在字符串与特殊类型数据之间转换,同样,总所周知,字符串在js中是基本数据类型String,拷贝基本类型数据,将在栈中开辟一个新空间并拷贝内容进其中。

那么,就可以将一个引用类型数据转换为json字符串形式的数据,将这字符串先赋值给一个暂存变量,再将暂存变量中的json字符串转换为一个引用类型数据并赋值给一个新变量,就可以实现引用类型数据深拷贝。如下代码示例。

//自定义一个较为复杂的对象
const jsonObj = {
  name: '测试对象2',
  list: [1,2,3,4,5],
  post: {
    title: '测试标题',
    content: '测试内容'
  },
  music: [
    {
      mtitle: '晴天',
      mauthor: '周杰伦'
    },
    {
      mtitle: '美人鱼',
      mauthor: '林俊杰'
    }
  ]
}

const jsonBuffer = JSON.stringify(jsonObj);//对象转换为json字符串,并赋值给一个暂时存储用的变量。
const jsonObj1 = JSON.parse(jsonBuffer);//将暂存变量中json字符串重新转换为对象,并赋值给一个新的变量。
									/*拷贝完成*/
//以下是测试
console.log(jsonObj);
/*
{
  name: '测试对象2',
  list: [ 1, 2, 3, 4, 5 ],
  post: { title: '测试标题', content: '测试内容' },
  music: [
    { mtitle: '晴天', mauthor: '周杰伦' },
    { mtitle: '美人鱼', mauthor: '林俊杰' }
  ]
}
*/
console.log(jsonObj1);
/*
{
  name: '测试对象2',
  list: [ 1, 2, 3, 4, 5 ],
  post: { title: '测试标题', content: '测试内容' },
  music: [
    { mtitle: '晴天', mauthor: '周杰伦' },
    { mtitle: '美人鱼', mauthor: '林俊杰' }
  ]
}
*/
//两者内容一样,实现了拷贝
console.log(jsonObj === jsonObj1);
//两个变量引用的不是同一个对象,实现了深拷贝。

总结

写的有点多,一直敲的没停,手疼。

标签:obj,对象,JS,中深,引用,测试,拷贝,数据
From: https://www.cnblogs.com/szq233/p/17013737.html

相关文章

  • 前端jsp界面一些固定模板
    <%@pagelanguage="java"contentType="text/html;charset=UTF-8"pageEncoding="UTF-8"%><%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%><ht......
  • 4js高级
    目录第一章 61作用域 62作用域链 63闭包 64变量提升 65函数提升 74动态参数arguments 75剩余参数 75.1剩余参数和arguments的区别 76箭头函数 86.1语法: 86.2箭......
  • 2js基础
    目录1JavaScript是什么 42js三种编写位置 43js注释 44js输入输出语句 44.1输出的三种方式 44.2输入【弹框输入】 55字面量 56变量 56.1变量声明 66.2变量赋值 66......
  • JS笔记(二):数据类型
    镇楼图Pixiv:torino三、数据类型原始类型原始类型像是string、symbol、number之类的都只能存储原子值,而不能像对象一样随意扩展。但是为了提供额外功能,采取了轻量的......
  • js三级联动,看不懂的请去我上一篇js的一些认识里瞅瞅
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>HTMLDOM</title></head><body><!--三级联动演示:省:四川,吉林,陕......
  • 1、在线留言+js验证
    <h2>客户留言</h2><divclass="liuyan"> <formaction=""id="form"> <ul> <li><span>您的姓名:</span><inputid="username"type="text"class="message_in&quo......
  • JS表单效验
    什么需要表单验证?1.减轻服务器的压力2.保证数据的完整性、有效性表单效验的步骤:1.获取表单元素输入的值2.对表单数据进行判断处理3.使用事件对数据进行提交表单选......
  • Js利用正则表达式去除字符串的中括号
    原文链接:点我  //功能:1)去除字符串前后所有空格   //     2)去除字符串中所有空格(包括中间空格,需要设置第2个参数为:g)   functionTrim(str,is_g......
  • js 复制图片
    //点击复制二维码functioncopyPic(url){varcanvas=document.createElement('canvas')//创建一个画板constimage=newImage()i......
  • 【C++要笑着学】类的默认成员函数详解 | 构造函数 | 析构函数 | 构造拷贝函数
     [本篇博客热榜最高排名:7 ]写在前面 朋友们好啊,今天终于更新了。我是柠檬叶子C,本章将继续讲解C++中的面向对象的知识点,本篇主要讲解默认成员函数中的构造函数、析构函......