js之深拷贝、浅拷贝

浅拷贝

对于基本类型,浅拷贝是对值的复制,对于对象来说,浅拷贝只复制指向某个对象的指针,而不复制对象本身,并没有开辟新的栈,也就是复制的结果是新旧对象还是共享同一块内存,两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变。

深拷贝

深拷贝会开辟新的栈,创造一个一模一样的对象,两个对象对应两个不同的地址,不共享内存,修改一个对象的属性,不会改变另一个对象的属性。

基本类型 和 对象类型
他们最大的区别就是在于他们的传值方式。 基本类型是传值 对象类型就是传引用。

<script>
    var a = 100;
    var b = a;
    b = 200;
    console.log(a);   //100
    console.log(b);   //200
    //修改a的时候,b不会改变
   //基本类型是按值传递,像是这样:在修改a时并不会改到b

    var obj1 = {
        a:100,
        b:200,
        c:300
    }
    var obj2 = obj1;
    obj2.b = 3647367;  //修改b
    console.log(obj1);   //{a: 100, b: 3647367, c: 300}
    console.log(obj2);   //{a: 100, b: 3647367, c: 300}
    //但对象就不同,对象传的是按引用传值
    //因为他们根本是同一个对象,这就是所谓的浅拷贝。
</script>

这里复制一份obj叫做obj2, 这里修改了obj2的b为3647367同时也修改了obj1.b。 因为他们本来就是一个对象 这就是所谓的浅拷贝。
避免这样的情况 我们这样写

var obj1 = {
        a: 100,
        b: 200,
        c: 300
    }
    var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }
    obj2.b = 3647367;  //修改b
    console.log(obj1);   //{a: 100, b: 200, c: 300}
    console.log(obj2);   //{a: 100, b: 3647367, c: 300}

这就是深拷贝 不会改到原来的obj1。

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

    如何做到深拷贝

    要完全复制又不能修改到原对象,这时候就要用深拷贝,下面是深拷贝的几种方式:

  • 1、自己手动复制
    像上面的范例,把obj1的属性一个个复制到obj2中:
var obj1 = {
        a: 100,
        b: 200,
        c: 300
    }
    var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }
    obj2.b = 3647367;  //修改b
    console.log(obj1);   //{a: 100, b: 200, c: 300}
    console.log(obj2);   //{a: 100, b: 3647367, c: 300}

但这样很麻烦要自己慢慢复制,而且这样其实不是深拷贝,如果像下面这个状况。

var obj1 = { body: { a: 10 } };
var obj2 = { body: obj1.body };
obj2.body.a = 20;
console.log(obj1);     // { body: { a: 20 } } <-- 被改到了
console.log(obj2);     // { body: { a: 20 } }
console.log(obj1 === obj2);      // false
console.log(obj1.body === obj2.body);    // true

虽然obj1跟obj2是不同对象,但他们会共享同一个obj1.body,所以修改obj2.body.a时也会修改到旧的。

    1. Object.assign
      Object.assign是 ES6 的新函数,可以帮助我们达成跟上面一样的功能。
    var obj1 = { a: 10, b: 20, c: 30 };
    var obj2 = Object.assign({}, obj1);
    obj2.b = 100;
    console.log(obj1);    // { a: 10, b: 20, c: 30 } <-- 沒被改到
    console.log(obj2);   // { a: 10, b: 100, c: 30 }

Object.assign({}, obj1)的意思是先建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj1。因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的深拷贝,不过如果要复制的对象只有一层的话可以考虑使用它。
如下有嵌套对象时,则没有办法做到深拷贝:

    var obj1 = { a: 0 , b: { c: 0}};
    var obj2 = Object.assign({}, obj1);
    console.log(obj2); // { a: 0, b: { c: 0}}

    obj1.a = 1;
    console.log(obj1);  // { a: 1, b: { c: 0}}
    console.log(obj2);   // { a: 0, b: { c: 0}}

    obj2.a = 2;
    console.log(obj1);    // { a: 1, b: { c: 0}}
    console.log(obj2);    // { a: 2, b: { c: 0}}

    obj2.b.c = 3;
    console.log(obj1);    // { a: 1, b: { c: 3}}
    console.log(obj2);    // { a: 2, b: { c: 3}}
  • 3、转成 JSON 再转回来
    用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);    // { body: { a: 10 } } <-- 沒被改到
console.log(obj2);    // { body: { a: 20 } }
console.log(obj1 === obj2);     // false
console.log(obj1.body === obj2.body);      // false

这样做是真正的Deep Copy,但只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。

var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);  // 'function'
console.log(typeof obj2.fun);  // 'undefined' <-- 没复制

要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。

  • 4、jquery的$.extend

jquery 有提供一个$.extend可以用来做深拷贝:

var $ = require('jquery');
var obj1 = {    a: 1,    b: { f: { g: 1 } },    c: [1, 2, 3] };
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);  // false

5、lodash的_.cloneDeep
函数库lodash提供_.cloneDeep用来做深拷贝:

var _ = require('lodash');
var obj1 = {    a: 1,    b: { f: { g: 1 } },    c: [1, 2, 3] };
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);     // false

这是比较推荐的方法,性能还不错,使用起来也很简单。

参考:https://www.cnblogs.com/lmjZone/p/8521443.html
https://www.cnblogs.com/syomm/p/5903740.html

原文地址:https://www.cnblogs.com/jessie-xian/p/11596081.html

时间: 2024-10-08 18:26:06

js之深拷贝、浅拷贝的相关文章

4个方面彻底说清JS的深拷贝/浅拷贝

首先,本文适用于业务需要,急需知道如何深拷贝JS对象的开发者.第二类,希望扎实JS基础,将来好去面试官前秀操作的好学者. 第一类 你只需要一行黑科技代码就可以实现深拷贝 var copyObj = { name: 'ziwei', arr : [1,2,3] } var targetObj = JSON.parse(JSON.stringify(copyObj)) 此时 copyObj.arr !== targetObj.arr 已经实现了深拷贝 别着急走,利用window.JSON的方法做深拷

js对象深拷贝浅拷贝

对象的深拷贝于浅拷贝 对于基本类型,浅拷贝过程就是对值的复制,这个过程会开辟出一个新的内存空间,将值复制到新的内存空间.而对于引用类型来书,浅拷贝过程就是对指针的复制,这个过程并没有开辟新的堆内存空间,只是将指向该内存的地址进行了复制.然而对引用类型的浅拷贝会出现一个问题,那就是修改其中一个对象的属性,则另一个对象的属性也会改变.产生了问题那必然有相对解决的方法, 就这样深拷贝就开始入场了,深拷贝会开辟新的栈,两个对象对应两个不同的地址,这样一来,改一个对象的属性,也不会改变另一个对象的属性.

JS中深拷贝与浅拷贝的区别,实现深拷贝的几种方法

JS中深拷贝与浅拷贝的区别,实现深拷贝的几种方法 如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力. 此篇文章中也会简单阐述到栈堆,基本数据类型与引用数据类型,因为这些概念能更好的让你理解深拷贝与浅拷贝. 我们来举个浅拷贝例子: let a=[0,1,2,3,4], b=a; console.log(a===b); a[0]=1; console.log(a,b); 嗯?明明b复

JS 中深拷贝的几种实现方法

JS 中深拷贝的几种实现方法1.使用递归的方式实现深拷贝 //使用递归的方式实现数组.对象的深拷贝 function deepClone1(obj) { //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝 var objClone = Array.isArray(obj) ? [] : {}; //进行深拷贝的不能为空,并且是对象或者是 if (obj && typeof obj === "object") { for (key i

深拷贝&浅拷贝

STRING.h文件 #pragma once #include<string.h> class String { public: String(char* str="")      //深拷贝 :_str(new char[strlen(str)+1]) { strcpy(_str, str); cout << "构造函数 " << endl; } ~String() { if (_str!=NULL) { delete[]_s

探讨一下iOS中深拷贝&amp;浅拷贝&amp;copy的那些事儿

什么是深拷贝?什么是浅拷贝? 为什么经常看到字符串属性要这样定义,那个copy是神马意思? @property(nonatomic,copy)NSString* name; 为什么下面的写法是错误的? @property(nonatomic,copy)NSMutableString* name; copyWithZone方法又到底是干嘛用的? 接下来,我们将一起,一步一步的去揭晓问题的答案. Copy到底是个啥? 其实我们真的没必要把copy想的太高深.它之所以叫copy,其终极目的已不言而喻,

深拷贝 浅拷贝 引用计数

;深拷贝 浅拷贝 深拷贝(成员用到了指针存储空间地址)每个对象的成员都有自己独立的成员内存地址空间,造成了浪费 浅拷贝,把原对象的指针也直接拷贝过来我还是用的这一片空间,但是析构会有重复释放问题,解决重复释放用引用计数记录这个类产生了多少对象,析构的时候--引用计数就可以了 计数为0   delete 这块内存空间 ;引用计数技术 优点:所有对象共享同一片空间,间接的达到了对象间的数据共享 缺点:一旦一个对象改变了这片内存,那么所有的对象都受到影响 ;写时拷贝技术 当对象需要操作这一块空间存放数

深拷贝 浅拷贝 以及赋值运算符= 的重载

在一些程序当中,需要将一个对象里的值直接传递给另一个对象,需要进行对象的拷贝.但是在某些情况下也会出现错误,如类内部成员需要动态开辟内存,实行位拷贝,就是将一个对象的值完全复制给另一个对象,如A=B,但如果B中也有一个指针申请了内存,那么A中的成员变量也指向了同一块内存,如果当释放资源的时候,就会导致野指针的出现,出现错误. 深拷贝的简单理解 就是在复制对象内容的过程中,需要重新分配资源,而浅拷贝则是不要重新分配资源.但是浅拷贝存在着问题:当资源释放时会产生资源归属不清的问题,导致运行错误. 当

c# 通用类型系统 深拷贝 浅拷贝 函数传参

c# 通用类型系统 及变量在 深拷贝 浅拷贝 函数传参 中的深层次的表现 在编程中遇到了一些想不到的异常,跟踪发现,自己对于c#变量在内存上的表现理解有偏差,系统的学习并通过代码实验梳理了各种情况下,变量在内存级的表现情况,对以后的coding应该有些帮助.在此记录以免忘记了... 1. 通用类型系统 先来一张图: 通用数据类型分为了值类型和引用类型. 我们定义一个int型实际上是一个system.int32的实例,在语法上我们像使用其他的类对象一样,但是,存储的的仍然是基本类型.这种把基本类型

【JS】深拷贝与浅拷贝的区别,实现深拷贝的几种方法

如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果A没变,那就是深拷贝,自食其力. 此篇文章中也会简单阐述到栈堆,基本数据类型与引用数据类型,因为这些概念能更好的让你理解深拷贝与浅拷贝. 我们来举个浅拷贝例子: let a=[0,1,2,3,4], b=a; console.log(a===b); a[0]=1; console.log(a,b); 嗯?明明b复制了a,为啥修改数组a,数组b也跟着变了,这里我不