js深浅拷贝

113 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

关于深浅拷贝其实涉及到js的数据类型及其存储,js中的数据类型有基本类型和引用类型,基本类型的值直接存储在栈内存当中,而引用类型在栈内存中储存的是地址,其值是存放在堆内存当中。

深拷贝和浅拷贝只针对像Object和Array这样的引用数据类型。简单来说,浅拷贝只复制一层对象的属性,如果对象内还有对象,那么只能复制嵌套对象储存在栈内存中的地址;而深拷贝则递归复制了所有嵌套层级的属性。下面我们看下具体实现。

浅拷贝

浅拷贝的简单实现:

function shallowCopy (obj) {
  var temp = {};
  for(var prop in obj) {
  // hasOwnProperty只会检查属性是否在对象中,而 in操作符(prop in obj)会检查属性是否在对象及其原型链中
    if(obj.hasOwnProperty(prop)) {
      temp[prop] = obj[prop];
    }
  }
  return temp;
}

var obj = {a: 1, b: [2,3];
var copyObj = shallowCopy(obj);

因为浅拷贝只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.b 和 copyObj.b 指向同一块内存地址,这样当copyObj更改属性b的值时,原对象obj的属性b值也会改变,如下:

copyObj.b[1] = 5;
obj.b[1]   // 5

深拷贝

而深拷贝则不同,深拷贝不仅会将原对象的各个属性拷贝出去,而且还会将原对象各个属性中所嵌套的对象也依次采用深拷贝的方法递归拷贝到新的对象上,这就不会存在浅拷贝中obj和copyObj的b属性指向同一个对象的问题。这样,copyObj中属性的更改也不会影响到原对象obj。

深拷贝的简单实现:

function deepCopy (obj) {
  if(obj == null) { return obj; }
  var temp = {};
  // in 会遍历当前对象上的属性和__proto__ 上的属性
  for(var prop in obj) {
    if(obj.hasOwnProperty(prop)) {
      if( typeOf obj[prop]=== 'object' ) {
        // 判断如果属性是对象,就进行递归
        temp[prop] = deepCopy(obj[prop])
      } else {
        temp[prop] = obj[prop];
      }
    }
  }
  return temp;
}

可以在深拷贝的实现当中看到,会通过typeOf对对象中的属性进行判断,如果嵌套属性还是对象,那么对其进行递归复制,如果不是,则直接进行复制。这样,新复制对象的改变就不会影响原对象。如下:

copyObj.b[1] = 5;
obj.b[1]   // 2