前端克隆

深克隆(深拷贝)/浅克隆(浅拷贝)是我们在学习js基础的时候就接触到的东西。今天我们一起回归复习下!

在JavaScript中可以将数据类型分为两种:基本数据类型和引用数据类型。基本数据类型的值直接存储在栈内存中。引用类型在栈中仅仅只是存了一个引用。真正的数据存储在堆内存中。下面我们来分析下这两种数据类型。

浅克隆(浅拷贝)

基本数据类型
let a = 1,b = a;
b = 3;
console.log(a,b); // 1,3

通过上面的例子我们总结出:基础数据类型都是独立的。也就是改变的时候不受其他影响

引用数据类型
let obj = {a:1,b:2};
let obj2 = obj1;
obj.a = 3;
console.log(obj1); // {a:3,b:2}
console.log(obj2); // {a:3,b:2}

通过上面的例子我们总结出:我们使用引用赋值的时候,只是将obj1的引用赋值给了obj2,这两个对象指向的地址是堆栈中的同一个地方,也就是
同一个数据。所以当我们修改的时候本质上修改的是一个数据。所以修改一个引用对象中的值两个都会变。

总结:浅拷贝只是拷贝对象的引用,并没有拷贝对象具体的值,所以当多个多个对象引用同一个值得时候,改变其中一个就会造成其他值得改变。

深克隆(深拷贝)

为了解决上面浅拷贝的问题出现了深拷贝!深拷贝可以作用在object数据类型中,深拷贝在使用的时候拷贝的是将引用类型的值拷贝过来生成一个新的引用类型。这样每次拷贝都生成新的对象,就不会产生使用的时候相互影响!

下面我们来简单展示下深拷贝的实例

JSON.parse(JSON.stringify(Object))方法实现深拷贝
let obj1 = {a:1,b:2};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a = 3;
console.log(obj1); // {a:1,b:2}
console.log(obj2); // {a:3,b:2}
修改之后obj1和obj2之间并没有什么影响
优点:简单便捷只需要一行代码
缺点:JSON.parse(JSON.stringify(Object))这种方法不能拷贝undefind,function,RegExp(正则表达式)这几个类型
Object.assign(target,source)方法

Object.assign()是es6中方法,用来将源对象(source)的所有可枚举属性复制到目标(target)属性上。它至少需要两个对象作为参数,第一个参数是目标对象。后面的参数都是源对象,如果只有一个target对象的时候回直接返回这个对象

let target1 = {a:1,b:2},
    source1 = {c:3},
    source2 = {d:4};
Object.assign(target1,source1);
console.log(target1); // {a:1,b:2,c:3}
Object.assign(target1,source1,source2);
console.log(target); //{a:1,b:2,c:3,d:4}

如果目标对象与源对象有同名属性或多个源对象有同名的属性。后面的属性就会覆盖前面的属性。

如果该参数不是对象会先转换成对象然后在返回

console.log(Object.assign(1)); {1}

注意:undefined和null作为参数的时候回直接报错,因为他们不能转成对象。
如果非参数对象参数出现在源对象位置的时候,这些参数会首先全转成对象。如果不发转成对象就会跳过。这意味着如果undefined和null不在首
参数就不会报错。其他类型的值(数值,字符创,布尔值)不在首参数也不会报错。除了字符串会以数组形式拷贝入目标对象,其他值都不会产生效果

Object.assign()只是拷贝自身属性,不可枚举属性和集成属性不会被拷贝。
对于嵌套对象。Object方法只是替换。而不是添加

let obj1 = {a:{b:1}}
let obj2 = {a:{c:2}}
Object.assign(obj1,obj2)
console.log(obj1) {a:{c:2}}

优点:为对象添加属性,为对象添加方法,克隆对象,合并多个对象,为属性指定默认值
缺点:object拷贝的是属性值。如果源对象的属性值是一个对象的引用。那么他也指向的是对象引用,修改的时候还是会出现问题。
如果想要保持克隆对象继承链。可以使用下面的方法

function clone(origin) {
     let originProto = Object.getPrototypeOf(origin);
     return Object.assign(Object.create(originProto), origin);
 }
 
 使用Object.getPrototypeOf()调用父类方法。获取原型对象(返回内部[[Prototype]]属性的值),如果没有继承属性返回null
 然后使用Object.create()根据返回的原型对象创建一个新对象。
递归递归拷贝
// 定义一个深拷贝函数  接收目标target参数
function lone(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;
}

第三中方法是最靠谱彻底的克隆,大部分数据类型都能拷贝成功

克隆的方法还有很多,这里我们主要介绍一下常用的方法!如果大家有什么好的克隆方法欢迎交流~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值