浅谈 深拷贝

前言
前段时间在掘金上看到 [深拷贝的终极探索(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]是什么意思呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值