zoukankan      html  css  js  c++  java
  • 深拷贝的几个误区

      周末赋闲在家,因为太冷了,不想出门,索性宅一天好了。但是闲着没事做总是很无聊的,正好新的一年想抓一下童鞋同学的代码质量,就随便打开了几个童鞋写的代码。于是故事就展开了。

      团队大了之后,如何统一团队代码风格其实是一个蛮重要的问题,目前我们团队使用lint的方式进行了限制,这次的review可以说是初见成效,除了不少同学偷偷摸摸的通过noverify的方式提交代码以外。不过没看多久就发现了一段有趣的代码:

    function deepClone(obj, res = {}) {
        const _res = res;
        for(let key in obj) {
            if (obj[key] == obj) {
                continue;
            }
    
            if (typeof obj[key] === 'object') {
                if(Array.isArray(obj[key])){
                    _res[key] = obj[key].slice();
                } else {
                    _res[key] = deepClone(obj[key], _res[key]);
                }
            } else {
                _res[key] = obj[key];
            }
        }
        return _res;
    }
    

      初看上去就会好奇,为什么不使用lodash现成的深拷贝呢?一看是个h5的项目,推测可能是为了整体包的大小做了取舍,也无可厚非吧。不过仔细看代码,乍一看好像还挺好,还细心的考虑的数组的情况,但是再仔细看得时候又觉得好像有什么地方不对,如果入参是个字符串感情你给别人返回一个空对象么。。跑了一个case发现果然有点问题:

    var c = { a:1 };
    var d = new Map();
    d.set('a', 1);
    var a = {
        a: 1,
        b: true,
        c: ()=>{console.log(123)},
        d: [1,2],
        e: d,
        f: c,    
        g: {} 
    }
    var b = deepClone(a);
    

      

      1、虽然正常的处理好像都没有什么问题,但是遇到新的数据结构如Map的时候,这种拷贝就会出问题;

      2、另外,它只处理了单层循环引用的情况,多层的时候情况会更复杂;

      3、而且这样递归,层级一深还会有爆栈的隐患,相当的不安全...

      4、虽然它对单独处理的数组的拷贝,但如果数组的某项的值是一个对象,它这样的处理依然有问题...

      所以深拷贝究竟应该怎么写呢?

      本着能google不手写的原则,查了下网络,好的写法没发现几个,倒是几个误区经有的文章经常会提到且一笔略过:

      1、JSON.parse(JSON.stringify(obj)) 的实现究竟算不算深拷贝?

        当然算,但是这种实现有几个潜在风险:

          1)它的原理是将能够JSON化的值JSON化,再重新生成一个新的JSON对象。所以它能够实现的基础是这个值是能够被JSON化的,像诸如function、map、set全是不能JSON化的,一转就没了。

          2) 它还有一个风险是在处理循环引用时是会报错的。这点很多童鞋在实操的时候特别容易忽略,特别是在node端进行端端通信的时候,曾经一个报错查半天,真的是血的教训。

        所以,如果是纯JSON的数据的深拷贝且不包含循环引用,是可以使用这个方法的

      2、递归在js中是有风险的

         常见的实现都是基于递归的,但是递归本身在js的runtime,很容易因为层级过深而导致爆栈。

         而通常的方式则是通过“拍平”树级结构的对象成一个数组,来进行拷贝。

      

      3、深拷贝的情况因业务场景的定义会有不同  

        有些业务场景需要保持拷贝对象中的值的引用关系不变,而有些却要改变。另外,js的数据结构发展到今天,需要在拷贝时处理的边界情况已经很多了,你需要好好考虑清楚哪些情况需要怎么处理。

      

      其实话说回来,仔细看得话,你会发现第一种写法和jQuery中extend的方式其实是很像的,通常的情况也基本能覆盖了,不过在现在这个语境下,相比比较“安全”的实现深拷贝,还是建议使用lodash的cloneDeep(相比jQuery和underscore深拷贝大概60行左右的代码,lodash使用了近几百行代码,考虑了各种边界情况,也可谓是业界楷模了)

  • 相关阅读:
    【网摘】Data Warehousing and BI Introduction
    【网摘】OLAP and Business Intelligence (ROLAP vs. MOLAP vs. HOLAP)
    [Oracle Issues]Wrong Password for User for Host Credentials in EM
    [Oracle Utility] Adrian Billington’s data_dump
    [Oracle Data Cartridge Interface] UserDefined Aggregation Functions
    【网摘】MVP (Passive View and Supervising Controller)
    SQL Techniques – Columns to Rows, Rows to Columns
    [Oracle Mgmt] Query Archivelog Mode, Change Archivelog Dest, etc.
    [Oracle SQL]Greatest and Least
    [Oracle 9i] Case Expression and Case Statement in 9i
  • 原文地址:https://www.cnblogs.com/mfoonirlee/p/10229517.html
Copyright © 2011-2022 走看看