数组和对象的深克隆和浅克隆

let obj1 = {
    name:'cdd'
    curse:{
        c1:'数学',
        c2:'语文'
    }
}
//let obj2 = obj1// 不是克隆,这只是让obj1/obj2共用一个堆内存,所谓克隆最起码是赋值一份一模一样的,是一个全新的堆内存。
console.log(obj1)

浅克隆

浅克隆:只克隆了第一级,对于后代级别还是公用之前的,新克隆的对象/数组还是可以改变原对象/数组中的内容

对象
  1. 循环遍历
let obj2 = {}
    keys = [
         ...Object.keys(obj1)
         ...Object.getOwnPropertySymbols(obj1)
    ]
keys.forEach(key => {
    obj2[key] = obj1[key]
})
console.log(obj2==obj1)//false
console.log(obj2.course==obj1.course)//true
  1. 基于展开运算符
let obj2 = {
    ...obj1
}
console.log(obj1==obj2)//false
console.log(obj1.course==obj2.course)//true
  1. 基于Object.assign
//Object.assign([obj1],[bj2]);返回的结果依然是obj1堆内存,只不过是把obj2中的键值对和原始obj1的键值对合并在一起了
//let obj2 = Object.assign(obj1,{})//obj2==obj1
let obj2 = Object.assign({},obj1)
console.log(obj2==obj1)//false
console.log(obj2.course==obj.course)//true
数组
  1. 循环遍历
let arr1 = [10,20,[30,40]]
let arr2 = []
arr1.forEach((item,index) => {
    arr2[index] = item
}
//使用map...
arr2 = arr1.map(item => item)
console.log(arr1 === arr2)//false
console.log(arr1[2] === arr2[2])//true
  1. 基于展开运算符或者Object.assign都可以实现
  2. 基于slice/concat…方法
let arr2 = arr1.slice()
console.log(arr2 === arr1)//false
console.log(arr2[2] === arr[2])//true

深克隆

深克隆:当前级及其所有的后代级别,最后都会克隆一份一模一样的,和原始的数据结构不存在任何的关联
,会消耗内存空间

  1. 基于JSON.stringify/JSON.parse
//基于JSON.stringify,把原始对象(或者数组)变为一个字符串
// 再基于JSON.parse,把字符串转换为对象,此时对象对应每个级别的堆内存都是全新开辟的
let obj2 = JSON.parse(JSON.stringify(obj1))
let arr2 = JSON.parse(JSON.stringify(arr1))

console.log(obj2 === obj1)//false
console.log(obj1.course === obj2.course)//false

console.log(arr1 === arr2)//false
console.log(arr1[2] === arr2[2])//false
基于JSON的方法实现深克隆,存在的问题:「因为JSON.stringify变为字符串,很多类型是不支持的」
  • 正则/Math对象/ArrayBuffer会被处理为空对象
  • 具备函数/Symbol/undefined属性值的属性直接被干掉了
  • BigInt还处理不了,会报错Uncaught TypeError: Do not know how to seriaize a BigInt.
  • 日期对象最后还是字符串

实现浅克隆

// 获取所有的私有属性,包含Symbol私有属性
function getOwnPropertys(obj) {
    if (obj == null) return []
    return [
        ...Object.keys(obj)
        ...Object.getOwnPrpertySymbols(obj)
    ]
}
// 浅克隆
function shallowClone (obj) {
    // 处理其它类型的值克隆
    let type =toType(obj)
    if(/^(number|string|boolean|null|undfined|symbol|bigint)$/.test(type)) return obj
    if(/^function$/.test(type)){
        //返回一个不同的函数,但是最后执行的效果和原始函数一致
        return function proxy(){
            return obj()
        }
    }
    if(/^(regexp|date)$/.test(type)) return new
    obj.constructor(obj)
    if(/^error$/.test(type)) return obj.constructor(obj.message)
    //...
    //只处理数组(最后返回的是数组)和对象(普通对象/类数组对象等都返回普通对象)
    let keys = getOwnPropertys(obj),
    clone = {}
    Array.isArray(obj) ? clone = [] : null
    keys.forEach((item,index) => {
        clone[item] = obj[item]
    })
    return clone
}

实现深克隆

// 检测数据类型的方法封装
(function () {
    var class2type = {};
    var toString = class2type.toString;

    [
        "Boolean",
        "Number",
        "String",
        "Symbol",
        "Function",
        "Array",
        "Date",
        "RegExp",
        "Object",
        "Error"
    ].forEach(function (name) {
        class2type["[object " + name + "]"] = name.toLowerCase();
    });

    function toType(obj) {
        if (obj == null) {
            return obj + "";
        }
        return typeof obj === "object" || typeof obj === "function" ?
            class2type[toString.call(obj)] || "object" :
            typeof obj;
    }
    window.toType = toType;
})();

function deepClone(obj,cache = new Set()) {
    // 只有数组和对象,我们再处理深克隆,其余的情况直接按照浅克隆处理即可
    let type = toType(obj)
    if(/^(object|array)$/.test(type)) return shallowClone(obj)
    // 避免自己套用自己导致的无限递归
    if (cache.has(obj)) return shallowClone(obj)
    cache.add(obj)
    let keys = getOwnPropertys(obj),
    clone = {}
    type === 'array' ? clone = [] : null
    keys.forEach(key => {
        clone[key] = deepClone(obj[key],cache)
    }) 
    return clone
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值