轻松掌握深拷贝和浅拷贝

首先,咱们必须明确一点,就是JavaScript的变量能够分为如下两种类型:javascript

  • 基本类型
    • undefined
    • null
    • number
    • string
    • boolean
    • symbol

基本变量是直接按值存放的,存放在栈内存中的简单数据段,能够直接访问。java

  • 引用类型 Object

存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另外一个位置。当须要访问引用类型(如对象,数组等)的值时,首先从栈内存中得到该对象的地址指针,而后再从堆内存中取得所需的数据。正则表达式

一个简单的例子数组

var a = 2;
var obj1 = {b:2};
var obj2 = obj1;

obj2.b = 3; 

console.log(obj1.b); // 3
console.log(obj2.b); // 3
复制代码

那么问题就来了,有一些场景咱们须要将一个对象含的值所有Copy给另外一个对象,这个时候若是只是简单的赋值操做,只是对指针进行了一个复制,而在堆内存区的值并无发生改变。因此咱们获得如下的结论:bash

什么是深拷贝

深拷贝便是在堆内存区拷贝出一个对象来。函数

深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操做互不影响。工具

实现浅拷贝

以前一直有一个错误实现深拷贝的想法,就是遍历一个对象的k-v对并一一复制给另外一个对象即可以实现深拷贝ui

可是是错误的,这个是浅拷贝(shallowCopy)spa

缘由很简单,当K-V对里valueObject的时候,复制过去便仍然是复制引用。好比prototype

let obj = {
    a: 1,
    b:{
        c: 2,
        d: 3
    }
}

let obj2 = {}

for(let item of Object.keys(obj)){
    obj2[item] = obj[item]
}

obj2.b.d = 2; 

obj.b.d // 此时obj.b.d 变成了2
复制代码

另一点

Object.assign也是一个shallowCopy

let obj1 = { a: 0 , b: { c: 0}};
  let obj2 = Object.assign({}, obj1);
  console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
  
  obj1.a = 1;
  console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
  console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
  
  obj2.a = 2;
  console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
  console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
  
  obj2.b.c = 3;
  console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
  console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
  
复制代码

如何实现深拷贝

  • 方法一:JSON对象的parse和stringify(最简单的)
let obj1 = { a: 0 , b: { c: 0}};

let obj2 = JSON.parse(JSON.stringify(obj1));

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

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

复制代码

该方法够处理JSON格式能表示的全部数据类型,可是没法拷贝对象里面的函数正则表达式等,并且会丧失全部的constructor,也就是说,将是破坏整条prototype链。

  • 方法二:递归浅拷贝

刚才提到浅拷贝只能拷贝对象的一层,那么对浅拷贝进行递归即可以实现深拷贝。

function deepCopy (oldObj, newObj){
    for(let key in oldObj){
        if(typeof oldObj[key] != 'object'){
            // 是基本类型直接复制
            newObj[key] = oldObj[key];
        }else {
            newObj[key] = oldObj[key].constructor == '[Function: Array]'?[]:{};
            deepCopy(oldObj[key],newObj[key])
        }
    }
}
复制代码

arguments.calle能够在匿名函数中实现递归,此处也能够用deepCopy(oldObj[key],newObj[key]),该方法的缺陷是,一旦欲拷贝对象和原对象存在相互引用的状况,即可能形成死循环。(一直往下递归仍然判断为Object即形成死循环)因此须要加上判断跳出的语句

...
for(let key in oldObj){
    if(newObj[key] === oldObj[key]){
        continue;
    }
    ...
}
...
复制代码
  • 方法三:借用工具库loadash

loadash有一个.cloneDeep的方法能够实现深拷贝。

使用方法:

obj2 = _.cloneDeep(obj1);

综上:

  • 基本类型变量存贮在栈内存区,存放在堆内存中的对象,变量保存的是一个指针。
  • 直接遍历对象一一复制是浅拷贝(shallowCopy)
  • 深拷贝便是在堆内存区拷贝出一个对象来。
  • 深拷贝固然更占内存,请必定要针对不一样的场景作不一样的拷贝处理。
相关文章
相关标签/搜索