首页 > 编程语言 >JavaScript复习记录(2)— 浅拷贝&深拷贝

JavaScript复习记录(2)— 浅拷贝&深拷贝

时间:2024-07-10 10:58:53浏览次数:9  
标签:obj 复习 对象 age JavaScript return 拷贝 name

1、前情概要

1.1、基本数据类型

        Number、String、Boolean、Null、Undefined、Symbol、BigInt。基本数据类型是直接存储在栈中的数据。

1.2、 引用数据类型

        Object、Array、Function、Date、RegExp、Map、Set、WeekMap、WeekSet、Promise、Error、Buffer。引用数据类型栈中存储的是该对象在栈中的引用(地址),真实的数据存储在堆中。

2、浅拷贝和深拷贝

  • 浅拷贝:拷贝一层,创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 深拷贝:将一个对象内存中完整的拷贝一份出来,从堆内存中开辟一个新的内存存放新对象,修改一个对象并不会影响原对象。

3、赋值、深拷贝、浅拷贝的区别 

前提是针对引用类型

  • 赋值:当把一个对象赋值给一个新的变量时,赋值的其实是该对象在栈中的地址,而不是堆中的数据。两个对象指向的是同一个存储空间,无论哪个对象发生改变,都是改变同一个存储空间的所有内容,两个对象联动。
  • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一个内存,会相互影响。
  • 深拷贝:从堆内存中开辟一个新的区域存放新对象,堆对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。

赋值例子:

let objOne = {
    age:20,
    name:["张三","李四"]
}

let objTwo = objOne;

objTow.age = 18;

obj.name[0]="王二";

console.log(objOne); // {age:18,name:["王二","李四"]}

console.log(objTwo); // {age:18,name:["王二","李四"]}

浅拷贝例子:

let objOne = {
    age:20,
    name:["张三","李四"]
}

let objThree = shallowClone(objOne);

objThree.age = 18;

obj.objThree[0]="王二";

console.log(objOne); // {age:20,name:["王二","李四"]}

console.log(objThree); // {age:18,name:["王二","李四"]}

// 这是个浅拷贝的方法
function shallowClone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i];
        }
    }
    return target;
}

深拷贝例子:

let objOne = {
    age:20,
    name:["张三","李四"]
}

let objFour = deepClone(objOne);

objFour.age = 18;

objFour.name[0]="王二";

console.log(objOne); // {age:20,name:["张三","李四"]}

console.log(objFour); // {age:18,name:["王二","李四"]}

// 这是个深拷贝的方法
function deepClone(obj) {
    if (obj === null) return obj; 
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    if (typeof obj !== "object") return obj;
    let cloneObj = new obj.constructor();
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        // 实现一个递归拷贝
        cloneObj[key] = deepClone(obj[key]);
      }
    }
    return cloneObj;
}

原数据:objOne  {age:20,name:["张三","李四"]}

赋值操作之后的:

原数据:bjTwo  {age:18,name:["王二","李四"]}

赋值数据:objTwo  {age:18,name:["王二","李四"]}

浅拷贝之后的:

原数据:objOne  {age:20,name:["王二","李四"]}

浅拷贝数据:objThree  {age:18,name:["王二","李四"]}

深拷贝之后的:

原数据:objOne  {age:20,name:["张三","李四"]}

深拷贝数据:objFour  {age:18,name:["王二","李四"]}

和原数据是否指向同一对象第一层数据为基本数据类型原数据中包含的子对象
赋值改变会使原数据一同改变改变会使原数据一同改变
浅拷贝改变不会使原数据一同改变改变会使原数据一同改变
深拷贝改变不会使原数据一同改变改变不会使原数据一同改变

4、浅拷贝的实现方式

  1. Object.assign()
  2. 函数库lodash的_.clone方法
  3. 展开运算符...
  4. Array.prototype.concat()
  5. Array.prototype.slice()

5、深拷贝的实现方式

  1. JSON.parse(JSON.stringify())
  2. 函数库lodash的_.cloneDeep方法
  3. jQuery.extend()方法

  4. 手写递归方法

原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是很拷贝

对象存在循环引用的情况,即对象的属性直接的引用了自身的情况;解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝。

关于这块学习,请仔细阅读ConardLi大佬如何写出一个惊艳面试官的深拷贝?这篇文章。

下面代码为只考虑到循环引用的情况:

function clone(target, map = new Map()) {
    if (typeof target === 'object') {
        let cloneTarget = Array.isArray(target) ? [] : {};
        if (map.get(target)) {
            return map.get(target);
        }
        map.set(target, cloneTarget);
        for (const key in target) {
            cloneTarget[key] = clone(target[key], map);
        }
        return cloneTarget;
    } else {
        return target;
    }
};

大佬写的更为牛的代码:

还在学习中...

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
  if (typeof obj !== "object") return obj;
  // 是对象的话就要进行深拷贝
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}
let obj = { name: 1, address: { x: 100 } };
obj.o = obj; // 对象存在循环引用的情况
let d = deepClone(obj);
obj.address.x = 200;
console.log(d);

标签:obj,复习,对象,age,JavaScript,return,拷贝,name
From: https://blog.csdn.net/qq_52050536/article/details/137152265

相关文章

  • [NodeJS] JavaScript模块化
    JavaScript诞生于1995年,一开始只是用于编写简单的脚本。随着前端开发任务越来越复杂,JavaScript代码也越来越复杂,全局变量冲突、依赖管理混乱等问题变得十分突出,模块化成为一个必不可少的功能。模块化发展史与方案对比YUI与JQuery2006年,雅虎开源了组件库YUILibrary,使用类似......
  • 【JavaScript脚本宇宙】状态管理利器:JavaScript 库全面解析
    提升项目效率与可维护性:JavaScript状态管理库大揭秘前言在现代前端开发中,状态管理是一个至关重要的话题。随着复杂性的增加,有效地管理应用程序的状态变得越来越具有挑战性。本文将介绍一些流行的JavaScript库,这些库提供了各种方式来管理状态和数据流。欢迎订阅专栏:Ja......
  • 代码随想录算法训练营第六十三天 | prim算法、kruskal算法、复习
    53.寻宝—prim算法题目链接:https://kamacoder.com/problempage.php?pid=1053文档讲解:https://programmercarl.com/kamacoder/0053.%E5%AF%BB%E5%AE%9D-prim.html思路本题是最小生成树的模板题,最小生成树可以使用prim算法,也可以使用kruskal算法计算出来。prim算......
  • 快速傅里叶变换复习笔记
    .real()成员函数FFT的本质是快速计算多项式的点值表示对负实数的四舍五入需要-0.5编写函数接收数组地址时,注意不能破坏原数组FFT有较为严重的精度问题,double甚至难以准确计算两个\(10^9\)级别的整数相乘的结果,即使采用longdouble也时常无法得到准确的答案,这或许也是模板题中......
  • JavaScript中的执行上下文和原型链
    目录一、执行上下文1.执行上下文2.执行上下文栈3.闭包1)定义2)形成条件3)例子(1)例子1:简单闭包(2)例子2:闭包与循环(3)例子3:使用闭包模拟私有变量二、原型链1.定义2.原型(Prototype)与构造函数(Constructor)3.原型链使用1)工作原理2)使用(1)设置原型对象(2)原型链的继承一、......
  • js浅拷贝与深拷贝的区别和实现方式
           ......
  • 浅拷贝和深拷贝的区别
    一、数据类型在讨论深浅拷贝之前,我们先说说数据类型,因为深浅拷贝与数据类型有关。数据类型分为基本数据类型(String、Number、Boolean、Null、Undefined、Symbol(es6引入的一种类型))和引用数据类型(Object、Array、Function)。基本数据类型特点:直接存储在栈中;引用数据类型:它真......
  • JavaScript基础笔记
    前言在JavaScript诞生的前几年,有人说:JavaScript是一门四不像的语言;JavaScript是一门没有规范的语言;JavaScript是一门兼容糟糕的语言;JavaScript是一门不精准的语言;JavaScript是一个半成品语言;JavaScript是一门糟糕的语言;JavaScript只是一个玩具胶水语言;这些声音从......
  • 简述 JS 中对象的创建和拷贝
    在JavaScript中,对象是一种非常重要且灵活的数据结构,用于存储多个值(属性)和方法(函数)对象的创建和拷贝是日常开发中经常涉及的操作,对于业务逻辑的准确实现有着重要的作用本文将简要概括JavaScript中对象的创建和拷贝方式,都是一些非常基础的知识,大家看个乐就好~目录对象的作......
  • 地理信息网络复习(GPT)已修改
    复习第一章概述WebGIS的概念以及系统架构:WebGIS(WebGeographicInformationSystem)是利用网络技术实现地理信息系(GIS)功能的系统。它结合了Web技术与GIS技术,用户可以通过浏览器访问和操作GIS数据。系统架构通常包括客户端、Web服务器、GIS服务器和数据库服务器等部分。W......