javascript的深拷贝VS浅拷贝

栏目: JavaScript · 发布时间: 7年前

内容简介:深拷贝VS浅拷贝本文主要对深拷贝&浅拷贝的解释及实现做一下简单记录。之所以会有深拷贝与浅拷贝之分,是因为不同数据类型的数据在内存中的存储区域不一样。

深拷贝VS浅拷贝

本文主要对深拷贝&浅拷贝的解释及实现做一下简单记录。 原文链接 ,欢迎star。

之所以会有深拷贝与浅拷贝之分,是因为不同数据类型的数据在内存中的存储区域不一样。

堆和栈是计算机中划分出来用来存储的区域,其中堆(heap)则是动态分配的内存,大小不定也不会自动释放;而栈(stack)为自动分配的内存空间,它由系统自动释放。存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配,是直接按值存放的,所以可以直接访问。

众所周知,JavaScript中的数据分为(基本类型和引用类型)。五种基本类型( boolean , number , undefined , null , string ,)的 数据的原始值是大小确定不可变 的,所以放在栈内存中;而引用类型(object)是放在堆内存中的,对应赋值的变量只是一个存放在栈内存中的指针,也就是一个指向引用类型存放在堆内存中的地址。

javascript的深拷贝VS浅拷贝

引用类型比较与基本类型比较的区别

引用类型是 引用的比较 ,例如

// a与b是两个引用类型,但其指针指向的是两个不同的引用
let a = [1,2,3];
let b = [1,2,3];
a===b; //false

// 引用类型的赋值操作,b的指针也是指向与a同样的引用
let a = [1,2,3];
let b = a;
a===b; //true

而基本类型间的比较是 值的比较 ,例如

let a = 1;
let b = 1;
a===b; //true

let a = 1;
let b = a;
a===b; //true

赋值操作:传值与传址的区别

在对基本类型进行赋值操作的时候实际是传值,即在栈内存中新开一个区域给新的变量,然后再把值赋给它。所以基本类型赋值的变量是两个互相独立变量。

let a = 10;
let b = a;
b++;
console.log(a); //10
console.log(b); //11

javascript的深拷贝VS浅拷贝

而引用类型的赋值操作是传址,只是在栈内存中新增了一个变量,同时赋值给这个变量的只是保存在堆内存中对象的地址,也就是指针的指向。因此这两个变量的指针指向同一个地址,相互操作也就会有影响。

let a = {};
let b = a;

a.name = 'slevin';
console.log(b.name); //'slevin'
console.log(a.name); //'slevin'
console.log(a===b); //true

javascript的深拷贝VS浅拷贝

需要注意的是,引用类型的赋值并不算是浅拷贝,因为赋值操作只相当于是引用,两个变量还是指向同一对象,任何一方改变了都会影响到另一方;但浅拷贝出来的变量与源变量已经不同,在不包含子对象的情况下(此情况即为深拷贝),一方改变不会影响到另一方。如下赋值操作:

let a = {};
let b = a;

b.name = 'slevin';
console.log(a.name); //'slevin';对b操作影响到了a
console.log(a===b); //true;因为两者指向同一个对象

浅拷贝实现方法

  1. 自定义实现方法:

    // 浅拷贝的方法
    function shallowCopy(srcObj) {
      let copy = {};
      for (let key in srcObj) {
          //只拷贝自身的属性,__proto__上继承来的属性不做拷贝,也可去掉这个限制
          if (srcObj.hasOwnProperty(key)) {
              copy[key] = srcObj[key];
          }
      }
      return copy;
    }
    
    let obj = {
      name: 'slevin',
      age: 18,
      language: {
          'english': 'good',
          'mandarin': 'wonderful',
      }
    }
    
    let copy = shallowCopy(obj);
    copy.age = 28;
    
    console.log(obj.age); // 18; 并没有改变源对象
    console.log(obj === copy); //false;两者指向不是同一个对象
  2. 利用 Object.assign(target,...sources) 可将一个或多个源对象上可枚举属性的值复制到目标对象,并返回目标对象。但注意, 只能做一层属性的浅拷贝

    let obj = {
      name: 'slevin',
      age: 18,
      language: {
          english: 'good',
          mandarin: 'wonderful',
          test: [1, 2, 3]
      },
      fn:function(){
          console.log('this:',this.name);
      }
    }
    
    let copy = Object.assign({},obj);
    //不会改变源对象
    copy.age = 22;
    //会改变源对象
    copy.language.english = 'bad';
    console.log('copy===obj:',copy===obj);//false
    console.log('copy:',copy);
    console.log('obj:',obj);
  3. 对于数组来说,也可以使用 Array.prototype.slice() 方法和 Array.prototype.concact() 方法

    let obj = [1,2,['a','b','c']]
    let copy = obj.slice();
    // 不会改变源对象
    copy[0]=111;
    // 会改变源对象
    copy[2][0]='aaa';
    console.log('copy===obj:',copy===obj);//false
    console.log('copy:',copy);
    console.log('obj:',obj);

深拷贝及实现方法

简单来说,深拷贝就是把对象 以及对象的子对象 进行拷贝。因为浅拷贝只复制了一层对象的属性,而如果对象的数值也为引用类型,那么拷贝过来依然是个引用地址,在拷贝对象上对子对象的值进行操作会改变原始数据中的值。

例如上面 obj 浅拷贝得到 copy对象 ,如果在 copy 对象上改变子对象 copy.language 上的属性值,会影响到源对象 obj

copy.language.english = 'bad';
copy.language.mandarin = 'bad';
console.log(obj.language); // {'english': 'bad','mandarin': 'bad',}

那么如何深拷贝呢?思路就是递归调用刚刚的浅拷贝,遍历所有值为引用类型的属性,然后依次赋值给另外一个对象即可。

  1. 方法一,自定义实现:

    /**将源对象source深度合并到目标对象target上

    • source 源对象
    • target 目标对象,默认{}
    • deep 是否深度合并,默认true

*/

function deepCopy (source,target={},deep=true) {
  for (let key in source){
      // 深拷贝,而且只拷贝自身属性.(默认传入的source为对象)
      if (deep && source.hasOwnProperty(key)){
          if (typeof(source[key])==='object'){
              // // source[key] 是对象,而 target[key] 不是对象, 则 target[key] = {} 初始化一下,否则递归会出错的
              // if (!(target[key] instanceof Object) && (source[key] instanceof Object)){
              // target[key] = {};
              // }
              // // source[key] 是数组,而 target[key] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的
              // if (!Array.isArray(target[key]) && Array.isArray(source[key])) {
              // target[key] = [];
              // }
              // 上面的代码可以简化为以下:
              target[key] = Array.isArray(source[key]) ? [] : {};

              // 递归执行拷贝
              deepCopy(source[key],target[key],true);
          } else {
              target[key] = source[key];
          }
      }
  }
  return target;
}


let obj = {
  name: 'slevin',
  age: 18,
  language: {
      english: 'good',
      mandarin: 'wonderful',
      test:[1,2,3]
  }
}
let copy = deepCopy(obj);
copy.language.test[0] = 111;

console.log('copy:',copy); //111 改变目标对象的子对象属性值
console.log('obj:',obj); //1 对应源对象上没有改变
  1. 利用 JSON.parse(JSON.stringify(copyObj)) 方法

    let obj = {
      name: 'slevin',
      age: 18,
      language: {
          english: 'good',
          mandarin: 'wonderful',
          test: [1, 2, 3]
      }
    }
    let copy = JSON.parse(JSON.stringify(obj))
    copy.language.test[0]=111;
    
    console.log('copy===obj:',copy===obj);//false
    console.log('copy.language.test[0]:',copy.language.test[0]);//111
    console.log('obj.language.test[0]:',obj.language.test[0]);//1

    但要注意,此方法 有两个缺点

    • 如果源对象属性值有函数,无法拷贝下来
    • 无法拷贝源对象原型链上的属性和方法
    let obj = {
      name: 'slevin',
      age: 18,
      language: {
          english: 'good',
          mandarin: 'wonderful',
          test: [1, 2, 3]
      },
      fn:function(){
          console.log('this:',this.name);
      }
    }
    let copy = JSON.parse(JSON.stringify(obj));
    console.log('copy===obj:',copy===obj);//false
    console.log('obj.fn:',obj.fn); //fn(){}...
    console.log('copy.fn:',copy.fn); //undefined

最后,再补一张引用类型的赋值操作、浅拷贝深拷贝对比图,加深印象

javascript的深拷贝VS浅拷贝

参考资料


以上所述就是小编给大家介绍的《javascript的深拷贝VS浅拷贝》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Making Things See

Making Things See

Greg Borenstein / Make / 2012-2-3 / USD 39.99

Welcome to the Vision Revolution. With Microsoft's Kinect leading the way, you can now use 3D computer vision technology to build digital 3D models of people and objects that you can manipulate with g......一起来看看 《Making Things See》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具