JavaScript 浅拷贝、深拷贝知识点整理

JavaScript中,数据类型分为基本数据类型和引用数据类型,对于基本数据类型来说,它的值直接存储在栈内存中,而对于引用来说,它在栈内存中仅仅存储来一个引用,而真正的数据存储在堆内存中。

一、基本数据类型

var a = 1;
var b = a;
b = 3;
console.log(a); //1
console.log(b); // 3

可以发现对于基本数据类型,我们将一个基本类型的值赋予a变量,接着将a的值赋予变量b;然后我们修改b,可以看到b被修改了,而a的值没有被修改,两个变量都使用的是独立数据。

二、引用数据类型

var obj1 = {
    a: 1,
    b: 2,
    c: 3
}
var obj2 = obj1;
obj2.a = 6;
console.log(obj1.a); // 6
console.log(obj2.b); // 6

可以发现两个对象的值全部被修改了。

对象是引用类型的值,对于引用类型来说,我们将obj1赋予obj2的时候,其实仅仅是将obj1存储在栈堆中的引用赋予了obj2,而两个对象此时指向的是在堆栈中的同一个数据,所以当我们修改任意一个值的时候,修改的都是堆内存中的数据,而不是引用,所以只要修改了,同样引用的对象的值也自然而然的发生了改变。

lodash.clone(value)。

创建一个value的浅拷贝。

下面的扩展运算符实现的也是浅拷贝。

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

以上都是浅拷贝。

对于浅拷贝而言,就是只拷贝对象的引用,而不深层次的拷贝对象的值,多个对象指向堆内存中的同一对象,任何一个修改都会使得所有对象的值修改。

深拷贝作用在引用类型上,如 Object, Array

深拷贝不会拷贝引用类型的引用,而是将引用类型的值全部拷贝一份,形成一个新的引用类型,这样就不会发生引用错乱的问题,使得我们可以多次使用同样的数据,而不用担心数据之间会起冲突。

以下为深拷贝的实现:

1.通过JSON对象实现深拷贝

var obj1 = {
    a: 1,
    b: 2,
    c: 3
}   

var objStr = JSON.stringfy(obj1)
var obj2 = JSON.parse(objStr)

obj2.a = 6;
console.log(obj1.a); // 1
console.log(obj2.a); // 6

可以发现没有发生引用问题,修改obj2的数据,并不会对obj1造成任何影响。

JSON对象实现深拷贝对一些问题:无法实现undefined、function、RegExp等类型等深拷贝。

2.Object.assign(target, source)

var obj1 = {
    a: 1,
    b: 2,
    c: 3
}
var obj2 = Object.assign({}, obj1);
obj2.a = 6;

console.log(obj1.a); // 1
console.log(obj2.a); // 6

这种方式实现看起来没有什么问题,但是这是一层对象,如果是多层嵌套呢

var obj1 = {
    a: 1,
    b: 2,
    c: ['a','b','c']
};
var obj2 = Object.assign({}, obj1);

obj2.c[0] = 'd';

console.log(obj1.c); // ["d","b","c"]
console.log(obj2.c); // ["d","b","c"]

可以看到对于一层对象来说没有任何问题,但是如果对象的属性对应是其它的引用类型的话,还是只拷贝了引用,修改的话还是存在问题。

3.递归拷贝

 // 定义一个深拷贝函数 接收目标target参数 
 function deepClone(target) {
     // 定义一个变量
     let result;
     // 如果当前需要深拷贝的是一个对象的话
     if( typeof target === 'object') {
         // 如果是一个数组
         if(Array.isArray(target)){
             //将result赋值为一个数组,并且执行遍历
             result = [];
             for(let i in target) {
                 // 递归克隆数组中的每一项
                 result.push(deepClone(target[i]))
             }
             // 判断如果当前的值是null的话,直接赋值为null
         } else if(target === null){
              result = null;
             // 判断如果当前的值是一个RegExp对象的话,直接赋值
         } else if (target.constructor === RegExp) {
              result = target;
             // 否则将是普通对象,直接for in循环,递归赋值对象的所有值
         } else {
              result = {};
              for(let i in target) {
                     result[i] = deepClone(target[i])
              }
         }
         // 如果不是对象的话,就是基本数据类型,那么直接赋值
     } else {
             result = target;
       }
       // 返回最终结果
       return result;
}

数据测试,查看效果:

let obj1 = {
    a: {
          b: null,
          c: 'c',
          d: undefined
    },
    b: function() {
         console.log(this.a)
    },
    c: [
         {
            a: 'a',
            b: /b/,
            c: undefined
         }
    ]
}
let obj2 = deepClone(obj1)
console.log(obj2)

可以发现最终拷贝的结果是null、undefined、function、RegExp等特殊的值也全部拷贝成功,而且修改也没有任何问题。

4.lodash函数库

lodash很热门的函数库,提供了lodash.cloneDeep()实现深拷贝,属于递归拷贝。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值