javascript篇-浅拷贝与深拷贝

理解javascript 的浅拷贝与深拷贝,首先看一下js的数据类型:

js有5种基本数据类型:undefined,null,boolean,number,string

还有一种复杂的数据类型(也叫引用类型),即 对象

1.对于基本数据类型:

他们的值在内存中占据着固定大小的空间,并被保存在栈内存中。当一个变量向另一个变量复制基本类型的值,会创建这个值的副本,并且我们不能给基本数据类型的值添加属性

上面代码中:a是基本数据类型(number),b是a的一个副本,它们两者都占有不同的内存空间,其中一个值的改变不会影响另一个值。

 

2.对于引用类型:

复杂的数据类型即是引用类型,它的值是对象,保存在堆内存中,包含引用类型值的变量实际上包含的不是对象本身,而是一个指向该对象的指针。从一个变量向另一个变量复制引用类型的值,复制的其实是指针地址而已,因此两个变量最终都指向同一个对象

 

上述代码中,将a的值赋值给b以后,b的值得改变也导致了a的值的改变,这其实就是浅拷贝

但是很多情况下这都不是我们要的效果,我们希望b的改变不会影响到a,这就是下面要将的深拷贝了,

(1) 数组的深拷贝

对于数组我们可以使用slice()和concat()方法来实现深拷贝,

 

(2) 对象的深拷贝

1:手动复制:

这种比较麻烦,而切属性多层嵌套的情况下,也必须要手动嵌套复制,否则,还是会改变

像这种对象只有一层的情况下可以使用Object.assign()函数(函数参考:https://blog.csdn.net/qs8lk88/article/details/79018481) 

 

3. 转成Json再转回来

 

这样做是真正的Deep Copy,这种方法简单易用。

但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。

这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。

 

4. 递归拷贝

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    if (typeof initalObj[i] === 'object') {
      obj[i] = (initalObj[i].constructor === Array) ? [] : {};            
      arguments.callee(initalObj[i], obj[i]);
    } else {
      obj[i] = initalObj[i];
    }
  }    
  return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

  

上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。

为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

改进版代码如下:

 

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

  

5、使用Object.create()方法

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}

 

直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。

6. jquery 有提供一个$.extend可以用来做 Deep Copy。

 

7. 另外一个很热门的函数库lodash,也有提供_.cloneDeep用来做 Deep Copy。

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

  

  

posted @ 2018-08-20 15:22  柠檬可乐  阅读(243)  评论(0编辑  收藏  举报