JS深浅拷贝

JS深浅拷贝的基础知识

js实现深浅拷贝的理论基础是,不同的数据类型在内存中存储的方式不同
js数据类型分为:基本数据类型,引用数据类型
基本数据类型:stringnumberbooleannullundefined(Symbol)
引用数据类型:objectobjectarrayfunctiondateRegExp

数据类型在内存中的存储形式,点击此链接查看详情

浅拷贝

基本数据类型的浅拷贝,不会有任何副作用和影响,实质是栈的 传值

var a = 100
var b = a
a = 200
console.log(a) // 200
console.log(b) // 100

引用数据类型的浅拷贝,修改任意一方,双方的值都会发生改变,实质是栈的 传址

var a = [1, 2, 3, 4, 5, 6]
var b = a
a[1] = 100
console.log(a) //[1, 100, 3, 4, 5, 6]
console.log(b) //[1, 100, 3, 4, 5, 6]

深拷贝

1 . 直接手动复制,赋值
(无任何副作用,就是很麻烦,递归实现深拷贝就是根据这个原理实现的)

obj1 = {a:1,b:2}
obj2 = {a: obj1.a, b: obj.b}

2 . 扩展运算符 ...Object.assign 这两个效果和缺点很相似,放在一起讲
扩展运算符...Object.assign缺点很明显,修改深层嵌套的对象属性时,源对象也会被修改,所以只适合修改第一层属性)

let obj = {
    a: 1,
    b: {
        b1: 'b1',
    },
    c: [1, 2, 3],
    d: function () {
        console.log(0)
    },
    e: undefined,
}
// let newObj = Object.assign({}, obj)
let newObj = { ...obj }
newObj.a = 'a'
newObj.b.b1 = 2  // 缺点处
newObj.c = [1, 2, 3, 4]
console.log(obj)
// {a: 1, b: {…}, c: Array(3), e: undefined, d: ƒ}
//     a: 1
//     b: {b1: 2}  // 缺点处
//     c: (3) [1, 2, 3]
//     d: ƒ ()
//     e: undefined
console.log(newObj)
// {a: "a", b: {…}, c: Array(4), e: undefined, d: ƒ}
//     a: "a"
//     b: {b1: 2}  // 缺点处
//     c: (4) [1, 2, 3, 4]
//     d: ƒ ()
//     e: undefined

3 . JSON.parse(JSON.stringify(obj))
优点:可以解决展开运算符...Object.assign()的缺陷,对多层嵌套也可以很好的拷贝
缺点:当objkey对应的valueObject某些特殊类型时,如Date对象、RegExp对象、Function函数时,会发现这种方法并不能完美的copy
虽然这种方法的缺点很明显,但仍是在已知返回数据的情况下的最优选择,也是最简单最常用的方法,一般接口返回的数据中也不会出现函数,日期对象等字段

let obj = {
    a: 1,
    b: {
        b1: 'b1',
    },
    c: [1, 2, 3],
    d: function () {     // 为函数时,会出现丢失
        console.log(0)
    },
    e: undefined,        // 为undefined时,会出现丢失
    f: RegExp('123'),    // 为RegExp对象时,会变成{}空对象
    g: new Date(),       // 为Date对象时,会转换成时间
}
let newObj = JSON.parse(JSON.stringify(obj))
newObj.a = 'a'
newObj.b.b1 = 2
newObj.c[2] = 'c3'
console.log(obj)
console.log(newObj)

在这里插入图片描述
4 . 使用第三方的库,如 lodash

var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

5 . 递归实现深拷贝
递归实现深拷贝的原理就是,利用递归,和手动赋值的形式,实现深拷贝,本质就是代码帮你执行了上面说到的第一种深拷贝的方法

实现思路:
(1.)如果是基本数据类型,直接返回
(2.)不是基本数据类型,判断是数组还是对象,并创建一个空数组或对象
(3.)如果是数组,循环数组对象,挨个将数组的值复制给刚才创建的空数组,如果子项是基本数据类型,直接复制,否则递归调用该方法
(4.)如果是对象,先用hasOwnProperty检测其自身属性,再执行(3)
(5.)返回刚开始定义的数组或对象

知识点:
1 . JS 判断数据类型的三种方法,这里使用的是第三种
2 . Array.prototype.slice 参数为负

/**
* 判断变量的类型
 * @param {object} value 变量值
 */
function checkType(value) {
    // slice(start,end)  自start到end(end不包括)
    // 序号从0开始,表示从第9位开始到倒数一位,也就是最后一位之前的截取
    return Object.prototype.toString.call(value).slice(8, -1)
}
// [object Undefined]
// 截取后 Undefined
// console.log(checkType(undefined))

/**
 * 深拷贝(递归)
 * @param {*} sourceValue 需要拷贝的值
 */
function deepClone(sourceValue) {
    // 如果传入的数据是简单类型(不是 {} & []),直接返回即可
    if (typeof sourceValue !== 'object') {
        return sourceValue
    }
    // 判断 传入参数的数据类型(object or array)
    let targetType = checkType(sourceValue)
    // 根据传入参数的数据类型,创建 初始存储结果的变量类型 {} or []
    let result = targetType === 'Object' ? {} : []
    // 遍历 sourceValue (for...in可以遍历数据和对象)
    // 避免数组内有自定义属性,遍历数组使用 for...of,遍历对象 for...in
    if (targetType === 'Array') {
        // 传入参数是数组时,次数使用的是 forEach 遍历,当然,也可以使用 数组的其他遍历方法
        sourceValue.forEach((value, index) => {
            let itemType = checkType(value)
            // 如果 value 是 数组 或 对象,则继续遍历
            if (itemType === 'Object' || itemType === 'Array') {
                result[index] = deepClone(value)
            } else {
                // 如果 value 是 基本数据类型 或者 函数,直接赋值即可
                result[index] = value
            }
        })
    } else {
        // 传入参数是对象时
        for (const key in sourceValue) {
            // 遍历数组时,key 为数组的 下标
            // 遍历对象时,key 为对象的 key
            // hasOwnProperty 只能检验对象自身的属性,不能检验继承属性,也不能检验原型链上的属性
            if (sourceValue.hasOwnProperty(key)) {
                const item = sourceValue[key]
                let itemType = checkType(item)
                // 如果 value 是 数组 或 对象,则继续遍历
                if (itemType === 'Object' || itemType === 'Array') {
                    result[key] = deepClone(item)
                } else {
                    // 如果 value 是 基本数据类型 或者 函数,直接赋值即可
                    result[key] = item
                }
            }
        }
    }
    // 返回 result 即可
    return result
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值