草庐IT

浅拷贝与深拷贝

不见水星记 2023-03-28 原文

一、数据类型存储

  • 在JavaScript中存在两大数据类型:基本类型、引用类型。

    • 基本数据类型存放在栈中,是一段简单的数据段,数据大小确定,内存空间大小可以分配,是直接按值存放的,可以按值访问

    • 引用数据类型存放在堆中,变量在栈中保存的是指向堆内存的地址值,这个地址值指向对应的对象类型,访问堆内存中的对象是通过地址值访问的。

二、浅拷贝

  • 浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。

  • 如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址。

  • 即浅拷贝是拷贝一层。

  • 下面简单实现一个浅拷贝:

    function shallowClone (obj) {
    	const newObj = {}
    	for (let prop in obj) {
    		if (obj.hasOwnProperty(prop)) {
    			newObj[prop] = obj[prop]
    		}
    	}
    	return newObj
    }
    
  • 在JavaScript中,存在浅拷贝的现象有:

    • Object.assign()
    • Array.prototype.slice()
    • Array.prototype.concat()
    • 使用扩展运算符实现的复制
  • Object.assign()

    var obj = {
        age: 18,
        nature: ['smart', 'good'],
        names: {
            name1: 'fx',
            name2: 'xka'
        },
        love: function () {
            console.log('fx is a great girl')
        }
    }
    var newObj = Object.assign({}, fxObj)
    
  • slice()

    const fxArr = ['One', 'Two', 'Three']
    const fxArrs = fxArr.slice(0)
    fxArrs[1] = 'love'
    consloe.log(fxArr)	// ['One', 'Two', 'Three']
    consloe.log(fxArrs)	// ['One', 'love', 'Three']
    
  • concat()

    const fxArr = ['One', 'Two', 'Three']
    const fxArrs = fxArr.concat()
    fxArrs[1] = 'love'
    consloe.log(fxArr)	// ['One', 'Two', 'Three']
    consloe.log(fxArrs)	// ['One', 'love', 'Three']
    
  • 扩展运算符

    const fxArr = ['One', 'Two', 'Three']
    const fxArrs = [...fxArr]
    fxArrs[1] = 'love'
    consloe.log(fxArr)	// ['One', 'Two', 'Three']
    consloe.log(fxArrs)	// ['One', 'love', 'Three']
    

三、深拷贝

  • 深拷贝开辟一个新的栈,两个对象相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

  • 常见的深拷贝方式有:

    • _.cloneDeep()
    • jQuery.extend()
    • JSON.stringify()
    • 循环递归
  • _.cloneDeep()

    const _ = require('lodash')
    const obj1 = {
    	a: 1,
    	b: { f: { g: 1 } },
    	c: { 1, 2, 3 }
    }
    const obj2 = _.cloneDeep(obj1)
    console.log(obj1.b.f === obj2.b.f)	// false
    
  • jQuery.extend()

    const $ = require('jquery')
    const obj1 = {
    	a: 1,
    	b: { f: { g: 1 } },
    	c: { 1, 2, 3 }
    }
    const obj2 = $.extend(true, {}, obj1)
    console.log(obj1.b.f === obj2.b.f)	// false
    
  • JSON.stringify()

    const obj = {
        name: 'A',
        name1: 'undefined',
        name2: function () {},
        name3:	Symbol('A')
    }
    const obj2 = JSON.parse(JSON.stringify(obj1))	// 会忽略undefined、Symbol、函数
    console.log(obj2)	// { name: 'A' }
    
  • 循环递归

    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
    }
    

四、区别

  • 浅拷贝只复制内存地址,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象。

    // 浅拷贝
    const obj1 = {
        name: 'init',
        arr: [1, [2, 3], 4]
    }
    const obj3 = shallowClone(obj1)	// 一个浅拷贝方法
    obj3.name = 'update‘
    obj3.arr[1] = [5, 6, 7]	// 新旧对象还是共享同一块内存
    console.log('obj1', obj1)	// obj1 { name: 'init', arr: [1, [5, 6, 7], 4] }
    console.log('obj3', obj3)	// obj3 { name: 'update', arr: [1, [5, 6, 7], 4] }
    
  • 深拷贝会另外创造一个一模一样的对象,新对象与原对象不共享内存,修改新对象不会改到原对象。

    // 深拷贝
    const obj1 = {
        name: 'init',
        arr: [1, [2, 3], 4]
    }
    const obj4 = deepClone(obj1)	// 一个深拷贝方法
    obj4.name = 'update‘
    obj4.arr[1] = [5, 6, 7]	// 新对象与原对象不共享内存
    console.log('obj1', obj1)	// obj1 { name: 'init', arr: [1, [2, 3], 4] }
    console.log('obj4', obj4)	// obj4 { name: 'update', arr: [1, [5, 6, 7], 4] }
    
  • 当拷贝类型为引用类型时:

    • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址。
    • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址。

有关浅拷贝与深拷贝的更多相关文章

  1. ruby - 为什么 Ruby 中没有深拷贝方法? - 2

    我正在研究技术图纸(svg/ruby)的解决方案。我想操作矩形,并且在这个类中有一个add!方法:classRectdefadd!(delta)@x1+=delta...#andsoonselfendend我还需要一个add方法返回一个Rect,但不操作self:defadd(delta)r=self.dup/clone/"copy"#dup和clone不做我的事但是:defcopy;Marshal.load(Marshal.dump(self));end会。为什么普通Ruby中不存在这样的基本功能?请不要告诉我我可以颠倒add和add!,让add完成工作,然后add!调用它。

  2. ruby-on-rails - Ruby 中数组的深拷贝 - 2

    我想在生产环境中获取一个对象,并将其精确复制(复制其内容)到另一个相同类型的对象。我尝试从ruby​​控制台以3种方式执行此操作,但均无效:假设您将tt作为要复制的第一个对象,并将tt2作为副本对象。我尝试的第一种方法是克隆数组tt2.patients=tt.urls.patientstt2.doctors=tt.segments.doctorstt2.hospitals=tt.pixels.hospitals我尝试的第二种方法是复制数组,这实际上与克隆数组相同:tt2.patients=tt.patients.duptt2.doctors=tt.doctors.duptt2.hosp

  3. 【JavaScript】手撕前端面试题:对象参数浅拷贝 | 简易深拷贝 | 完整深拷贝 - 2

    🖥️NodeJS专栏:Node.js从入门到精通🖥️博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述)🖥️TypeScript知识总结:TypeScript从入门到精通(十万字超详细知识点总结)🧑‍💼个人简介:大三学生,一个不甘平庸的平凡人🍬👉你的一键三连是我更新的最大动力❤️!文章目录1、浅拷贝要求思路代码2、简易深拷贝要求思路代码3、完整深拷贝要求思路代码1、浅拷贝要求补全JavaScript代码,要求实现一个对象参数的浅拷贝并返回拷贝之后的新对象。注意:参数可能包含函数、正则、日期、ES6新对象是对对象的参数进行浅拷贝,并不是直接对整个对象进行浅拷贝(整个

  4. ruby-on-rails - 如何在 Ruby 中创建对象的深拷贝? - 2

    我做了一些搜索,发现了一些关于创建深复制运算符的不同方法和帖子。在Ruby中是否有快速简单(内置)的深度复制对象的方法?这些字段不是数组或散列。使用Ruby1.9.2。 最佳答案 普通Ruby中并未内置深拷贝,但您可以通过编码和解码对象来破解它:Marshal.load(Marshal.dump(@object))但这并不完美,并且不适用于所有对象。更稳健的方法:classObjectdefdeep_clonereturn@deep_cloning_objif@deep_cloning@deep_cloning_obj=clone@

  5. javascript - forEach 在循环之前是否创建数组的深拷贝? - 2

    举个例子arr1=[{b:2},{a:1}]//anarraywith2elementsarr1.forEach(function(element,index,array){console.log(element);console.log('of');console.log(array);console.log('');arr1.push({c:3});});console.log(arr1);结果{b:2}of[{b:2},{a:1}]{a:1}of[{b:2},{a:1},{c:3}][{b:2},{a:1},{c:3},{c:3}]在上面的示例中,我正在遍历一个数组并向其添加更多

  6. javascript - 使用 jQuery 的 DOM 元素的非深拷贝 - 2

    是否可以在不克隆其内容的情况下在jQuery中创建DOM元素的克隆/副本?我需要将一个div的内容拆分为两个具有相同属性的独立div。因此,例如我需要更改:thisisthefirstsentence.thisisthesecondsentence.变成类似的东西:thisisthefirstsentence.thisisthesecondsentence.具体如何拆分内容比较复杂,但这基本上就是我需要做的。显然,可以使用以下方法创建没有内容的克隆:$(el).clone().empty();但由于我的元素可能变得相当大,我想摆脱不必要地克隆元素内容的开销。想法?谢谢!

  7. javascript - 对于 JavaScript 多维数组的深拷贝,深入一层似乎就足够了。这是真的吗? - 2

    注意:我只是一个编码新手,所以这个问题的核心可能存在明显的错误或误解。本质上,我需要在JavaScript中“按值”深度复制多维数组到未知深度。我原以为这需要一些复杂的递归,但似乎在JavaScript中您只需要深复制一个级别就可以按值复制整个数组。举个例子,这是我的测试代码,使用了一个故意复杂的数组。functiontest(){vararr=[['ok1'],[],[[],[],[[],[[['ok2'],[]]]]]];varcloned=cloneArray(arr);arr='';//Deletetheoriginalalert(cloned);}functioncloneA

  8. javascript - 浅拷贝对象遗漏了 ES6/ES7 中的一个或多个属性? - 2

    这就是我一直在做的:varprops={id:1,name:'test',children:[]}//copypropsbutleavechildrenoutvarnewProps={...props}deletenewProps.childrenconsole.log(newProps)//{id:1,name:'test'}有没有更干净、更简单的方法? 最佳答案 你可以使用destructuringassignment:varprops={id:1,name:'test',children:[]}var{children:_,.

  9. javascript - 结构化克隆算法与深拷贝有何不同 - 2

    有一个MDNarticle声明:ThestructuredclonealgorithmisanewalgorithmdefinedbytheHTML5specificationforserializingcomplexJavaScriptobjects.It'smorecapablethanJSON所以,我相信这意味着它比以这种方式克隆的能力更强:JSON.parse(JSON.stringify(obj))在thisthread中建议.JSON方式有很多缺点,例如不支持循环引用、丢弃JSON规范不支持的所有内容(如函数)以及将Date对象表示为字符串。然后我想到结构化克隆算法是很多库

  10. javascript - ES6 React - 引用、浅拷贝和深拷贝有什么区别以及如何比较它们? - 2

    我知道这个问题已经被讨论了很多次,我想我已经有了一个基本的想法。我从StackOverflow找到了一些评分最高的答案:Whatisthedifferencebetweenadeepcopyandashallowcopy?DeepCopyandShallowCopy但所有的答案对我来说似乎都很模糊。让我们考虑以下示例:constuser={name:"James",age:33,highlights:{career:"Basketballplayer",NBAChampion:[2012,2013,2016],},promotion:()=>("GetLeBron15now!"),};

随机推荐