深拷贝和浅拷贝

概念

首先我们要了解一下什么是栈和堆:
栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间。(基本类型:StringNumberBooleanNullUndefined
堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型,指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针。(引用类型:FunctionArrayObject

在JS中,数据类型分为基本数据类型和引用数据类型两种,对于基本数据类型来说,它的值直接存储在栈内存中,而对于引用类型来说,它在栈内存中仅仅存储了一个引用,而真正的数据存储在堆内存中。


栈数据

  var a = 3;
  var b = a;
  b = 5;

  console.log(a);  //3
  console.log(b);  //5

上面这段代码可以看到的是对于基本类型来说,我们将一个基本类型的值赋予a变量,接着将a的值赋予变量b;然后我们修改b;可以看到b被修改了,而a的值没有被修改,两个变量都使用的是独立的数据;


堆数据

  var obj1 = {
    a: 1,
    b: 2,
    c: 3
  }
  var obj2 = obj1;
  obj2.a = 5;

  console.log(obj1.a);  //5

可以看到的是,两个对象的值全部被修改了。对象是引用类型的值,对于引用类型来说,我们将obj1赋予obj2的时候,我们其实仅仅只是将obj1存储在栈堆中的的引用地址赋予了obj2,而两个对象此时指向的是在堆内存中的同一个数据,所以当我们修改任意一个值的时候,修改的都是堆内存中的数据,而不是引用,所以只要修改了,同样引用的对象的值也自然而然的发生了改变。
其实,上面的例子就是一个简单的浅拷贝


单层数据的深拷贝

方法一
  var obj1 = { a: 1, b: 1 }
  var objString = JSON.stringify(obj1); //把json对象转换成json字符串
  var obj2 = JSON.parse(objString);   //json字符串转换成json对象
  obj2.b = 4

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

但是使用JSON.stringify()以及JSON.parse(),是不可以拷贝undefinedfunctionRegExp等等类型


方法二
  var obj1 = {
    a: 1,
    b: 2,
  }
  var obj2 = Object.assign({}, obj1);
  obj2.b = 5;

  console.log(obj1);
  console.log(obj2);

可以看到对于一层对象来说是没有任何问题的,但是如果对象的属性对应的是其它的引用类型的话,还是只拷贝了引用,修改的话还是会有问题。


深拷贝实现

function deepClone(target) {
    // 定义一个变量
    let result;
    //  如果当前需要深拷贝的是一个对象的话
    if (typeof target === 'object') {
        // 如果是一个数组的话
        if (Array.isArray(target)) {
            result = []; // 将result赋值为一个数组,并且执行遍历
            for (let i in target) {
                // 递归克隆数组中的每一项
                result.push(deepClone(target[i]))
            }
            // 判断如果当前的值是null的话;直接赋值为null
        } else if (target === null) {
            result = null;
            // 判断如果当前的值是一个RegExp对象的话,直接赋值
        } else if (target.constructor === RegExp) {
            result = target;
        } else {
            // 否则是普通对象,直接for in循环,递归赋值对象的所有值
            result = {};
            for (let i in target) {
                result[i] = deepClone(target[i]);
            }
        }
        // 如果不是对象的话,就是基本数据类型,那么直接赋值
    } else {
        result = target;
    }
    // 返回最终结果
    return result;

使用

  var obj1 = {
    a: 1,
    b: 2,
    c: {
      name: 'aaron',
    },
  }

  let obj2 = deepClone(obj1);
  obj2.c.name = 'pipi';

  console.log(obj1);
  console.log(obj2);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容