javaScript中的深拷贝和浅拷贝的原理和实现方式

1.首先要清楚,深拷贝和浅拷贝只是在引用数据类型(obj,Function,Array等)中才会有区别,基本数据类型不适用。

基本数据类型:Boolean Null Undefined Number String Symbol (ES6) BigInt (ES10)
引用数据类型:object、array、function

2.为什么不适用呢?这里涉及到栈和堆的存储问题。
我们知道,基本数据类型的值都是在栈中,而引用数据类型在栈中存储的的只是地址,而值是存储是在堆中;引用数据类型的值都是通过栈中的地址找到堆中对应的值。

3.浅拷贝 – 例子

let arr = [1,2,3];
let arr2 =[];
arr2 = arr;
arr[0] = 10;
console.log(arr2 ,arr2);
打印出来结果后发现arr2 随着arr的改变而改变了:
控制台打印结果都是:[10,2,3] 

为什么两个数组之间的值会收到互相的干扰呢?
原理是这样:在引用数据类型中,一个引用数据把值赋给另一个引用数据类型,仅仅只是在栈中将引用的地址复制一遍,在栈中在开辟一个内存,存放一个相同的地址而已,但是并没有在堆中对应地开辟一块内存用来存放新的值。这就出现了两个地址指向同一块堆中的值,其实就是同一个值,无论是哪一有变动,两个取到的值都只是改变过后的最新的且是同样的值。当然,这在开发中肯定存在一定的问题,问了解决这个问题,我们不得不引进一个深拷贝的概念。

在介绍深拷贝之前,我先说说2个简单的实现浅拷贝的方法:

(1) Object.assign()
Object.assign我们经常会用到合并对象,当然利用Object.assign性质我们也可以实现对象的拷贝。

var obj1 = {a: 1, b: 2}
var obj2 = Object.assign({}, obj1)
obj2.a = 4
console.log(obj1, obj2)// {a: 1, b: 2}    {a: 4, b: 2}
这里要注意的是Object.assign第一个参数必须是个空对象 这时候的浅拷贝才算是拷贝成功
(2) 解构赋值
var obj1 = {a: 1, b: 2}
var obj2 = {...obj1}
obj2.a = 4
console.log(obj1, obj2)
这里一样可以实现之前上面的结果。

但这两种拷贝有一个问题就是只能赋值一层,假设我们有如下数据结构
var obj1 = [{
    name: '臧三',
    childs: ['小明', '小芳']
}]

var obj2 = [...obj1]
obj2[0].childs = []
console.log(obj1, obj2)
打印出来可以看出obj1,obj2 的结果均同样变了,这并不是我们想要的结果,所以我们要用到深拷贝。

4.深拷贝 – 例子
原理:通过以下方法,拷贝的对象不会相互收到影响,深拷贝就是把某一个对象的地址和值都拷贝一份,分别在栈中和堆中重新开辟一块属于自己的内存,无论是哪一个值得改变都不会影响另一个的对象的值。

深拷贝的实现方法:

(1)递归去复制所有层级属性

function deepClone(obj) {
  let objClone = Array.isArray(obj) ? [] : {}
  if (obj && typeof obj === 'object') {
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        // 判断obj子元素是否为对象,如果是,递归复制
        if (obj[key] && typeof obj[key] === 'object') {
          objClone[key] = deepClone(obj[key])
        } else {
          objClone[key] = obj[key]
        }
      }
    }
  }
  return objClone
}
 
let a = [1,2,3,4]
let b = deepClone(a)  // [1,2,3,4]
a[0] = 2  // [2,2,3,4]
b  //  [1,2,3,4]

(2)用JSON对象的parse和stringify(对函数不起作用)

function deepClone (obj) {
  let _obj = JSON.stringify(obj)
  let objClone = JSON.parse(_obj)
  return objClone
}
 
let a = [0,1,[2,3],4]
let b = deepClone(a)
a[0] = 1  
a[2][0] = 1  // [1,1,[1,3],4]
b  // [0,1,[2,3],4]

(3)JQ的extend方法,extend方法也可以拷贝对象

$.extend([deep ], target, object1 [, objectN ])
//deep表示是否深拷贝,为true为深拷贝;为false,为浅拷贝。
//target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
//object1  objectN可选。 Object类型 第一个以及第N个被合并的对象。 

let a = [0,1,[2,3],4]
let b = $.extend(true, [], a)
a[0] = 1
a[2][0] = 1  // [1,1,[1,3],4]
b  // [0,1,[2,3],4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追逐梦想之路_随笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值