资讯专栏INFORMATION COLUMN

关于JavaScript的浅拷贝和深拷贝

shenhualong / 2550人阅读

摘要:引用类型值引用类型值是保存在堆内存中的对象,变量保存的只是指向该内存的地址,在复制引用类型值的时候,其实只复制了指向该内存的地址。

前言

要理解 JavaScript中浅拷贝和深拷贝的区别,首先要明白JavaScript的数据类型。JavaScript有两种数据类型,基础数据类型和引用数据类型。
js的基本类型:undefined,null,string,boolean,number,symbol(es6新增),保存在栈内存中
js的引用类型:Object类型, Array类型,Date类型,RegExp类型,Function类型,基本包装对象(Boolean类型,Number类型,String类型),单体内置对象(Global对象,Math对象),保存在堆内存空间中

1.基本类型值和引用类型值的复制

1.1基本类型值

基本类型值是指在栈内存保存的简单数据段,在复制基本类型值的时候,会开辟出一个新的内存空间,将值复制到新的内存空间。

var a = 1;
var b = a;
a = 2;
console.log(a);//输出2;
console.log(b);//输出1;

var a = 1;

var b = a;

a = 2;

从上面例子看出,当一个变量的值是基本类型,把它复制给另一个变量,复制完成后改变它的值,不会影响已经复制了它的值的变量。

1.2引用类型值
引用类型值是保存在堆内存中的对象,变量保存的只是指向该内存的地址,在复制引用类型值的时候,其实只复制了指向该内存的地址。

var a = {
   name: "Kitty",
   age: "20",
   sex: "man"
};
var b = a;
a.name = "Jack";
console.log(a);//输出{name: "Jack",age: 20,sex: "man"}
console.log(b);//输出{name: "Jack",age: 20,sex: "man"}

var a = {name: ‘Kitty’,age: ‘20’,sex: ‘man’};

var b = a;

a.name = ‘Jack’;

从上面例子看出,当一个变量的值是引用类型值,把它复制给另外一个变量,复制的只是指向储存对象内存的地址,所以复制完成后,改变它的值,会影响复制了它的值的变量。
注意:如果有两个变量的值是引用类型值,就算它们的值完全相同,它们也是不相等的,因为它们指向的内存地址不同,例子:

2. 对象的浅拷贝与深拷贝

当一个变量是对象,如果像上面那样直接将一个变量赋值给另一个变量,如果改变某个变量的值,另外一个变量也会跟着改变,如果我们不想发生这种情况,就需要写一个函数用来拷贝对象。

深拷贝和浅拷贝的示意图大致如下:

2.1 对象浅拷贝

var a = {name:"wanger"}
var b = Object.assign({}, a)
a===b // false
b.name = "zhangsan"
a.name //"wanger"

上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆,这时候a与b指向的是不同的栈对象,所以对b.name重新复制也不会影响到a.name。但是如果a.name是一个对象的引用,而不是一个字符串,那么上面的代码也会遇到一些问题,参考如下代码:

var a = {name:{firstName:"wang",lastName:"er"}}
var b = Object.assign({}, a)
a===b // false
b.name.firstName = "zhang"
a.name.firstName //"zhang"

b.name.firstName又影响到了a.name.firstName,这是因为Object.assign()方法只是浅层拷贝,a.name是一个栈对象的引用,赋值给b时,b.name也同样是这个栈对象的引用,很多时候,我们不想让这种事情发生,所以我们就需要用到对象的深拷贝。

2.2 对象深拷贝

2.2.1 万能的for循环实现对象的深拷贝

var obj = {
  name: "FungLeo",
  sex: "man",
  old: "18"
}
var obj2 = copyObj(obj)
function copyObj(obj) {
  let res = {}
  for (var key in obj) {
    res[key] = obj[key]
  }
  return res
}

2.2.2 JSON.parse(JSON.stringify(objectToClone))

var obj = {
  name: "FungLeo",
  sex: "man",
  old: "18"
}
var obj2 = JSON.parse(JSON.stringify(obj))

2.2.3 扩展运算符实现对象的深拷贝

var obj = {
  name: "FungLeo",
  sex: "man",
  old: "18"
}
var { ...obj2 } = obj
obj.old = "22"
console.log(obj)
console.log(obj2)

运行结果如下:

3. 数组的浅拷贝与深拷贝

3.1 数组浅拷贝

var arr = ["old", 1, true, null, undefined];
var new_arr = arr.concat(); // 或者var new_arr = arr.slice()也是一样的效果;
new_arr[0] = "new";
console.log(arr); // ["old", 1, true, null, undefined]
console.log(new_arr); // ["new", 1, true, null, undefined]

数组的浅拷贝,可用concat、slice返回一个新数组的特性来实现拷贝,但是如果数组嵌套了对象或者数组的话用concat、slice拷贝只要有修改会引起新旧数组都一起改变了,比如:

var arr = [{old: "old"}, ["old"]];
var new_arr = arr.concat();
arr[0].old = "new";
new_arr[1][0] = "new";
console.log(arr); // [{old: "new"}, ["new"]]
console.log(new_arr); // [{old: "new"}, ["new"]]

如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或者数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化。这种叫浅拷贝 。
深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。

3.2 数组深拷贝

3.2.1 利用扩展运算符...对数组中嵌套对象进行深拷贝

var arr=[{a:1,b:2},{c:1,d:2}];
    var arr2=[];
    
    arr.forEach(item=>{
      var {...obj}=item;
      arr2.push(obj);
    })
    arr2[1].d=7
    
    console.log(arr,arr2)

3.2.2 利用lodash库的cloneDeep方法

var arr=[{a:1,b:2},{c:1,d:2}];
var arr2=_.cloneDeep(arr)   

arr2[1].d=7;
console.log(arr,arr2)

3.2.3 JSON.parse(JSON.stringify(objectToClone))

var arr=[{a:1,b:2},{c:1,d:2}];
var arr2=JSON.parse(JSON.stringify(arr));
    
arr2[1].d=7;
console.log(arr,arr2)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108613.html

相关文章

  • JavaScript的浅拷贝和深拷贝

    摘要:在中可以通过添加一个参数来实现递归,调用就可以实现一个深拷贝。利用序列化实现一个深拷贝 在JavaScript中,对于Object和Array这类引用类型值,当从一个变量向另一个变量复制引用类型值时,这个值的副本其实是一个指针,两个变量指向同一个堆对象,改变其中一个变量,另一个也会受到影响。 这种拷贝分为两种情况:拷贝引用和拷贝实例,也就是我们说的浅拷贝和深拷贝 浅拷贝(shallow...

    ernest.wang 评论0 收藏0
  • JavaScript的浅拷贝和深拷贝

    摘要:但是进行的是浅拷贝,拷贝的是属性值。对象展开符深拷贝的实现方式手动复制转成再转回来只有可以转成格式的对象才可以这样用,像没办法转成沒被改到使用方法避免相互引用对象导致死循环,如的情况四参考关于的浅拷贝和深拷贝 一、理解 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。...

    GeekQiaQia 评论0 收藏0
  • JS中的浅拷贝和深拷贝

    摘要:说明外层数组拷贝的是实例说明元素拷贝是引用深拷贝在堆中重新分配内存,并且把源对象所有属性都进行新建拷贝,拷贝后的对象与原来的对象完全隔离,互不影响。中的方法可以实现深拷贝,源码原理也是递归使用浅拷贝。 1.浅拷贝 当把数组或对象简单赋值给其他变量的时候,实际上进行的是浅拷贝,浅拷贝是拷贝引用,只是将拷贝后的引用指向同一个对象实例,彼此间的操作还会互相影响。 分为两种情况:直接拷贝源对象...

    xeblog 评论0 收藏0
  • 关于js的浅拷贝与深拷贝

    摘要:原文地址浅拷贝和深拷贝只针对像这样的复杂对象的简单来说,浅拷贝只拷贝一层对象的属性,而深拷贝则递归拷贝了所有层级。浅拷贝通过来实现浅拷贝。 原文地址:http://www.silenceboy.com/201... 浅拷贝和深拷贝只针对像Object, Array这样的复杂对象的.简单来说,浅拷贝只拷贝一层对象的属性,而深拷贝则递归拷贝了所有层级。 浅拷贝 通过 Object.ass...

    summerpxy 评论0 收藏0
  • js的浅拷贝和深拷贝和应用场景

    摘要:而大多数实际项目中,我们想要的结果是两个变量初始值相同互不影响。所以就要使用到拷贝分为深浅两种深浅拷贝的区别浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。 为什么会用到浅拷贝和深拷贝 首先来看一下如下代码 let a = b = 2 a = 3 console.log(a) console.log(b) let c = d = [1,2,3] let e = f = {a:...

    MartinDai 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<