面试问题:对象的深拷贝和浅拷贝区别以及详解

深拷贝和浅拷贝的区别
JavaScript中有两种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。
最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。

浅拷贝 —-只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
深拷贝 —-在计算机中开辟了一块新的内存地址用于存放复制的对象。

区别:浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;深拷贝是拷贝多层,每一级别的数据都会拷贝出来;



浅拷贝实例

//此递归方法不包含数组对象
var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);

function shallowCopy(src) {
  var newobj = {};
  for (var i in src) {
    if (src.hasOwnProperty(i)) {
      newobj[i] = src[i];
    }
  }
  return newobj;
}

因为浅复制只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.arr 和 shallowObj.arr 指向同一块内存地址。



深拷贝实例

深复制则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在上面 obj 和 shallowObj 的 arr 属性指向同一个对象的问题。

var obj = { 
    a:1, 
    arr: [1,2],
    nation : '中国',
    birthplaces:['北京','上海','广州']
};
var obj2 = {name:'杨'};
obj2 = deepCopy(obj,obj2);
console.log(obj2);
//深复制,要想达到深复制就需要用递归
function deepCopy(original,result){
   var result = result || {};
   for(var i in original){
   if(typeof original[i] === 'object'){
          //要考虑深复制问题了
          if(original[i].constructor === Array){
            //这是数组
            result[i] =[];
          }else{
            //这是对象
            result[i] = {};
          }
          deepCopy(original[i],result[i]);
        }else{
          result[i] = original[i];  //一般数据直接拷贝
        }
     }
     return result;
 }

这样obj和obj2分别拥有不同的内存地址,两边值的改变互不影响。

其他方法:

1.转成 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,这种方法简单易用。

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

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

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

2.上述递归方法改进版
上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。
为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

改进版代码如下:

var obj = { 
    a:1, 
    arr: [1,2],
    nation : '中国',
    birthplaces:['北京','上海','广州']
};
var obj2 = {name:'杨'};
obj2 = deepCopy(obj,obj2);
console.log(obj2);
//深复制,要想达到深复制就需要用递归
function deepCopy(original,result){
   var result = result || {};
   for(var i in original){
   var prop = original[i];  // 避免相互引用对象导致死循环
   if(typeof prop === 'object'){
          //要考虑深复制问题了
          if(prop.constructor === Array){
            //这是数组
            result[i] =[];
          }else{
            //这是对象
            result[i] = {};
          }
          deepCopy(prop,result[i]);
        }else{
          result[i] = prop;  //一般数据直接拷贝
        }
     }
     return result;
 }

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

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;

猜你喜欢

转载自blog.csdn.net/chenjuan1993/article/details/81913452