javaScript中关于深拷贝/浅拷贝的问题

目录

一、什么是浅拷贝和深拷贝?

二、浅拷贝

Object.assign(target, source) —浅拷贝

ES5 的 Object.create—浅拷贝

ES6 提供的扩展运算符...—浅拷贝

数组的 concat()和 slice()方法—数组浅拷贝

三、深拷贝

借助JSON.parse、JSON.stringify实现深拷贝

 手动实现,递归拷贝—深拷贝


一、什么是浅拷贝和深拷贝?

浅拷贝制是指只复制一层对象的属性,不会复制对象中的对象的属性,对象的深拷贝会复制对象中层层嵌套的对象的属性。(单来说浅复制只复制一层对象的属性,而深复制则递归复制了所有层级。)

二、浅拷贝

  • Object.assign(target, source) —浅拷贝

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

但是如果对象的属性对应的是其它的引用类型的话,还是只拷贝了引用,修改的话还是会有问题

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

所以是浅拷贝。

Tips:

  • 只拷贝了自身的可枚举属性,没有拷贝正确的原型和不可枚举属性。
  • IE不兼容
  • ES5 的 Object.create浅拷贝

利用Object.create(), 我们能拷贝出一个高度相似的对象:

let clone = Object.create(
	    Object.getPrototypeOf(target), 
        Object.getOwnPropertyDescriptors(target)
    );

这种拷贝在对象与对象直接几乎是完美的,正确的原型,正确的属性。但遗憾数组拷贝的结果会变成一个对象,尽管数组的访问方式和方法都可以,但Array.isArray(clone)===false

  • ES6 提供的扩展运算符...—浅拷贝

ES6中的展开语法...能方便地进行对象属性复制,同assign(),它拷贝了对象自身的可枚举属性

let aa = {
	age: 18,
	name: 'aaa'
}

let bb = {...aa};
bb.age = 22;

console.log(aa.age); // 18

只进行了一层浅拷贝
这等同于使用Object.assign()方法,如果对象的属性对应的是其它的引用类型的话,还是只拷贝了引用,修改的话还是会有问题。

  • 数组的 concat()和 slice()方法—数组浅拷贝

  1. concat()这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。(不传参数的情况下,它只是复制当前数组并返回副本。)
  2. slice(),它能够基于当前数组中的一或多个项创建一个新数组。
var newArray = ['a',1,2,{name:'heyushuo'}];
var clone1 = newArray.concat();
var clone2 = newArray.slice(0);
clone1[3].name = "kebi";

console.log(clone2.name) //kebi
console.log(newArray.name) //kebi
//通过上边例子可以看到如上两个方法实现的是浅复制

三、深拷贝

  • 借助JSON.parse、JSON.stringify实现深拷贝

JSON 对象是 ES5 中引入的新的类型(支持的浏览器为 IE8+),JSON 对象 parse 方法可以将 JSON 字符串反序列化成 JS 对象,stringify 方法可以将 JS 对象序列化成 JSON 字符串,借助这两个方法,也可以实现对象的深拷贝

let copyObj = JSON.parse(JSON.stringify(target));

虽然这个方法在很多时候都能派上用场。但是,也存在问题:

  1. 对某些数据不支持:如Date类型会被转为字符串类型,Undefined和RegExp类型丢失等问题。
  2. 无法拷贝存在循环引用的对象。
  3. 拷贝自身可枚举字符串属性,原型链丢失。
  4. 属性特性丢失。
  5. 性能较差。
  •  手动实现,递归拷贝—深拷贝

// 定义一个深拷贝函数  接收目标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;
        }else {
         // 否则是普通对象,直接for in循环,递归赋值对象的所有值
            result = {};
            for (let i in target) {
                result[i] = deepClone(target[i]);
            }
        }
     // 如果不是对象的话,就是基本数据类型,那么直接赋值
    } else {
        result = target;
    }
     // 返回最终结果
    return result;
}

// vue中全局挂载使用
 Vue.prototype.$deepClone = deepClone
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

回忆哆啦没有A梦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值