deepClone

作者: 吴晗君 | 来源:发表于2019-03-26 22:59 被阅读0次

    JSON

    在 JSON 中,值必须是以下数据类型之一:

    字符串
    数字
    对象(JSON 对象)
    数组
    布尔
    Null

    JSON 的值不可以是以下数据类型之一:

    函数
    日期
    undefined

    这种方式有弊端,只能解析JSON数据,会丢失其他类型的数据。

    JSON.parse(JSON.strigify(value))
    
    JSON.stringify({a: () => {}}) === '{}' // true
    

    jQuery.extend

    这是jQuery.extend源代码,如果出现循环引用的问题,会直接报栈溢出错误 Uncaught RangeError: Maximum call stack size exceeded

    jQuery.extend = jQuery.fn.extend = function () {
      var options,
        name,
        src,
        copy,
        copyIsArray,
        clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false
    
      // Handle a deep copy situation
      if (typeof target === 'boolean') {
        deep = target
        target = arguments[1] || {}
        // skip the boolean and the target
        i = 2
      }
    
      // Handle case when target is a string or something (possible in deep copy)
      if (typeof target !== 'object' && !jQuery.isfunction(target)) {
        target = {}
      }
    
      // extend jQuery itself if only one argument is passed
      if (length === i) {
        target = this
        --i
      }
    
      for (; i < length; i++) {
        // Only deal with non-null/undefined values
        if ((options = arguments[i]) != null) {
          // Extend the base object
          for (name in options) {
            src = target[name]
            copy = options[name]
    
            // Prevent never-ending loop
            if (target === copy) {
              continue
            }
    
            // Recurse if we're merging plain objects or arrays
            if (
              deep &&
              copy &&
              (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))
            ) {
              if (copyIsArray) {
                copyIsArray = false
                clone = src && jQuery.isArray(src) ? src : []
              } else {
                clone = src && jQuery.isPlainObject(src) ? src : {}
              }
    
              // Never move original objects, clone them
              target[name] = jQuery.extend(deep, clone, copy)
    
              // Don't bring in undefined values
            } else if (copy !== undefined) {
              target[name] = copy
            }
          }
        }
      }
    
      // Return the modified object
      return target
    }
    
    

    有以下两个注意点

    1. 注意循环引用
      数组存储对象值,避免循环引用的问题。
    2. 除了对象和数组外的其他typeofobject的数据类型处理
      下面是我的实现,不过对其他数据类型处理不完整,做的比较好的是lodash,这是他的源码
    function deepCloneWrap (value) {
          let mapCache = []
          function deepClone (value) {
            if(value instanceof Date) return new Date(value);
            if(value instanceof RegExp) return new RegExp(value);
            // 这里还应该处理好多其他对象类型的数据
            if (typeof value !== 'object') return value
    
            // 如果和之前保存的引用一致则直接返回,则直接返回,避免循环溢出
            if (mapCache.length) {
              for (let i = 0,len = arr.length; i < len; ++i) {
                if (mapCache[i][0] === value) {
                  return mapCache[i][1]
                }
              }
            }
            // 直接取constructor,就不用判断是数组还是对象。
            let cloneVal = new value.constructor() 
            // 将要被克隆的值先存储起来,后面每次对象进入函数先进行比较
            mapCache.push([value, cloneVal])
            for (let k in value) {
              cloneVal[k] = deepClone(value[k])
            }
            return cloneVal
          }
          return deepClone(value)
        }
    
        let a = {b:{c:1}, date: new Date()}
        a.x = a
        let newA = deepCloneWrap(a)
        console.log(newA)
    
    

    用WeakMap,一方面是可以通过Map实例的has方法判断是否以及有过当初处理对象,另一方面也可以避免内存泄露。

    function deepClone (value, hash = new WeakMap()) {
          if(value instanceof Date) return new Date(value);
          if(value instanceof RegExp) return new RegExp(value);
          if (typeof value !== 'object') return value
    
          if (hash.get(value)) return hash.get(value)
          let cloneVal = new value.constructor()
          hash.set(value, cloneVal)
          for (let k in value) {
            cloneVal[k] = deepClone(value[k], hash)
          }
          return cloneVal
        }
    
        let b = {b:{c:1}}
        b.x = b
        let newB = deepClone(b)
        console.log(newB)
    

    相关文章

      网友评论

          本文标题:deepClone

          本文链接:https://www.haomeiwen.com/subject/cdyovqtx.html