浅谈 深拷贝

前言
前段时间在掘金上看到 [深拷贝的终极探索(90%的人都不知道)] 这篇文章,深受启发,但看到了,理解了,并不代表就会写了,于是就诞生了这篇文章,本文核心的跟上文差不多,不过我增加了 从不明白到明白的 资料。

深拷贝—浅拷贝
简单的说 深拷贝就是浅拷贝 + 递归

爆栈: 指递归中,存储的信息量大于系统栈的内存(简单来讲就是遍历的深度超限了,10000就会报错)。
循环引用的问题解决思路有两种,一直是循环检测,一种是暴力破解
JSON.stringify内部做了循环引用的检测破解了循环引用得问题(死循环)
lodash是如何解决循环应用这个问题?它是用一个栈记录了所有被拷贝的引用值。如果再次碰到同样的引用值的时候,不会再去拷贝一遍。而是利用之前已经拷贝好的值。)。

JSON.parse(JSON.stringify(obj)) 会干掉undefined项和function项;

其实暴力破解递归爆栈的方法有两条路,
第一种是消除尾递归,但在这个例子中貌似行不通,
第二种方法就是干脆不用递归,改用循环,

let obj = {
        x: 3,
		y: undefined,
		z:  function(){
				console.log(11111111)
		}
};
JSON.parse(JSON.stringify(obj))
// {x: 3}

Object.prototype.toString.call() 方法
获取对象类型–比typeof全面
比instanceof 兼容性 好

Object.assign() 会将数组 的索引拿来处理成对象(或者说 类数组)
Object.assign({}, [{a:null}])
{0: {…}}

深拷贝

1/简单实现

		function cloneDeep(obj) {

            if(typeof(obj) != 'object') return obj;
            let target = Object.prototype.toString.call(obj) == "[object Object]" ? {} : [];
            for(var i in obj) {
                if (obj.hasOwnProperty(i)) {
                    target[i] = typeof(obj[i]) == 'object' ?  cloneDeep(obj[i]) : obj[i]; 
                }
            }

            return target;

        }

        let t = [{a:2,b:4}]
        let k = cloneDeep(t);
        t[0].a = 7;

        console.log(k)

2/Object.assign() 和 Array.from()相结合
Array.from()将类数组转化为数组

const deepClone = obj => {
  if (obj === null) return null;
  let clone = Object.assign({}, obj);
  Object.keys(clone).forEach(
    key =>
      (clone[key] =
        typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
  );
  if (Array.isArray(obj)) {
    clone.length = obj.length;
    return Array.from(clone);
  }
  return clone;
};

解析:
const deepClone = obj => {
if(obj === null) return null;
let clone = Object.assign({}, obj); //如果obj是个数组也在此 换成对象
Object.keys(clone).forEach(key => {
clone[key] = typeof(clone[key])==‘object’?deepClone(clone[key]) : clone[key]
})
if(Array.isArray(obj)){ //如果obj是数组,就将 clone由对象换回数组格式
clone.length = obj.length;
return Array.from(clone)
}
return clone;
}

const a = [{ foo: ‘bar’, obj: { a: 1, b: 2 } }];
const b = deepClone(a); // a !== b, a.obj !== b.obj

console.log(b, 111)

3/借助栈遍历对象,若对象中还是对象就推入栈中,若对象中是基本数据类型则直接拷贝。

作为吃瓜群众看这个 root 为什么会产生变化? root与parent、parent与res 的关系 显得比较迷,看了一波,在这儿以自己的视角给后来者说一下:

  1. root 为什么会产生变化? 关键在于这儿 parent: root, parent 引用的是root,parent变了root 跟着变,两个是一样的;
  2. parent与res 的关系? 包含与被包含的关系,res是parent的一项。
    关键在这儿
    let res = parent;
    if (typeof key !== ‘undefined’) {
    res = parent[key] = {};
    }
    如果while遍历的不是初始值(undefined),则直接赋值
    res = parent[key] = {};
function deepCloneSelf(obj) {
            const target = {};
            const loopList = [
                {
                    parent: target,
                    key: undefined,
                    data: obj
                }
            ]

            while(loopList.length){
                let node = loopList.pop();
                let parent = node.parent;
                let key = node.key;
                let data = node.data;

                let res = parent;
                if(typeof(key) != 'undefined'){
                    res = parent[key] = {}
                }

                for (const k in data) {
                    if (data.hasOwnProperty(k)) {
                        // const element = object[key];
                        if(typeof(data[k]) == 'object'){
                            loopList.push({
                                parent: res,
                                key: k,
                                data: data[k]
                            })
                        }else{
                            res[k] = data[k]
                        }
                        
                    }
                }
            }

            return target
        }

备注:
js 中一切都是对象
const obj = {};
Object.prototype.toString.call(obj) === “[object Object]” //true

注意第二个变量(Object)的首字母大写(隐式调用 toString),小写的是方法返回的值的格式中默认的(写死的),大写的是对象类型的名称(数据类型)。

默认 返回 “[object objectname]”,其中 objectname 是对象类型的名称。

遍历对象属性的方法
for…in
Object.keys(obj)
Object.values(obj)
Object.getOwnPropertyNames(obj)
Object.getOwnPropertySymbols(obj)
Reflect.Ownkeys()

for…in(在Chrome Opera 中)遍历的时候会按照以下规则进行遍历
1.首先遍历数值建,按照升序排序
2.其次遍历字符串,按照加入时间的升序排列
3.最后遍历Symbol

在这里插入图片描述

参考
深拷贝的终极探索(90%的人都不知道)

js深拷贝的四种境界

实现深拷贝的几种方式

[object Object]是什么意思呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拷贝拷贝是针对JavaScript中的对象属性为对象的情况而言的。拷贝只是复制了对象的引用,也就是复制了内存地址,所以拷贝后的对象和原对象会共用同一个内存地址,属性值的改变会相互影响。而拷贝则是创建了一个完全独立的对象,拷贝了所有的属性和属性值,所以拷贝后的对象和原对象是完全独立的,互不影响。 在JavaScript中,可以通过不同的方法实现拷贝拷贝拷贝可以使用手动实现、Object.assign()和拓展运算符等方法。手动实现的拷贝方法是遍历原始对象的属性,将属性值赋给新对象。Object.assign()方法可以将一个或多个源对象的属性复制到目标对象中,也可以用于实现拷贝。拓展运算符(...)也可以用于实现拷贝拷贝则需要递归地复制对象的属性和属性值。可以通过手动实现一个拷贝函数,在遍历原始对象的属性时,如果属性值是对象类型,则递归调用拷贝函数进行复制。这样可以创建一个完全独立的对象,拷贝了所有的属性和属性值。 总结起来,拷贝只是复制了对象的引用,而拷贝是创建了一个完全独立的对象。使用不同的方法可以实现拷贝拷贝,根据具体的需求选择合适的方法进行使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [JavaScript拷贝拷贝概念与用法实例分析](https://download.csdn.net/download/weixin_38735570/14820137)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [javascript的拷贝拷贝](https://blog.csdn.net/weixin_47417033/article/details/124899204)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值