资讯专栏INFORMATION COLUMN

lodash之cloneDeep浅析

chemzqm / 3762人阅读

摘要:浅拷贝和深拷贝本质上的原因是对象引用的是地址,直接赋值会吧引用地址也复制给新值。深拷贝是会递归源数据,吧新值得引用地址给换掉。对递归对递归调用是否是类型数组这里主要是会有个的特殊数组返回的特殊数组其他方法库或等

浅拷贝和深拷贝

本质上的原因是对象引用的是地址,直接赋值会吧引用地址也复制给新值。
浅复制只会将对象的各个属性进行依次复制,会把引用地址也复制。
深拷贝是会递归源数据,吧新值得引用地址给换掉。

lodash的cloneDeep

入口



const CLONE_DEEP_FLAG = 1
const CLONE_SYMBOLS_FLAG = 4

function cloneDeep(value) {
  return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG)
}

核心逻辑

function baseClone(value, bitmask, customizer, key, object, stack) {
  let result
  const isDeep = bitmask & CLONE_DEEP_FLAG
  const isFlat = bitmask & CLONE_FLAT_FLAG
  const isFull = bitmask & CLONE_SYMBOLS_FLAG

  if (customizer) {
    result = object ? customizer(value, key, object, stack) : customizer(value)
  }
  if (result !== undefined) {
    return result
  }
  if (!isObject(value)) {
    return value
  }
  // 判断是否数组
  const isArr = Array.isArray(value)
  // 获取constructor
  const tag = getTag(value)
  if (isArr) {
    // 初始化一个长度和源相等的数组
    result = initCloneArray(value)
    // 不是deep就直接复制了事
    if (!isDeep) {
      return copyArray(value, result)
    }
  } else {
    const isFunc = typeof value == "function"
      // Buffer.isBuffer, 对于buffer对象就直接复制了事
    if (isBuffer(value)) {
      return cloneBuffer(value, isDeep)
    }
    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
      // 是否需要继承proto
      result = (isFlat || isFunc) ? {} : initCloneObject(value)
      if (!isDeep) {
        // 这里deepclone的isFlat是0,走copySymbols,这个方法主要是复制源上的Symbols
        return isFlat
          ? copySymbolsIn(value, copyObject(value, keysIn(value), result))
          : copySymbols(value, Object.assign(result, value))
      }
    } else {
      // 如果是func或error, WeakMap就直接返回了
      if (isFunc || !cloneableTags[tag]) {
        return object ? value : {}
      }
      // 对tag位true的类型进行clone
      result = initCloneByTag(value, tag, isDeep)
    }
  }
  // Check for circular references and return its corresponding clone.
  // 检查循环引用并返回其相应的克隆。
  stack || (stack = new Stack)
  const stacked = stack.get(value)
  if (stacked) {
    return stacked
  }
  stack.set(value, result)
  // 对map递归clone
  if (tag == mapTag) {
    value.forEach((subValue, key) => {
      result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack))
    })
    return result
  }
  // 对set递归调用
  if (tag == setTag) {
    value.forEach((subValue) => {
      result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack))
    })
    return result
  }
  // 是否是TypedArray类型
  if (isTypedArray(value)) {
    return result
  }

  const keysFunc = isFull
    ? (isFlat ? getAllKeysIn : getAllKeys)
    : (isFlat ? keysIn : keys)

  const props = isArr ? undefined : keysFunc(value)
  arrayEach(props || value, (subValue, key) => {
    if (props) {
      key = subValue
      subValue = value[key]
    }
    // Recursively populate clone (susceptible to call stack limits).
    assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack))
  })
  return result
}

数组这里主要是会有个exec的特殊数组

function initCloneArray(array) {
  const { length } = array
  const result = new array.constructor(length)

  // Add properties assigned by `RegExp#exec`.
  // RegExp.exec返回的特殊数组
  if (length && typeof array[0] == "string" && hasOwnProperty.call(array, "index")) {
    result.index = array.index
    result.input = array.input
  }
  return result
}
其他方法

Underscore库或JSON.parse等

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110196.html

相关文章

  • 前端面试题及答案 - JS篇

    摘要:中使用操作符具体做了哪些事情创建了一个空对象空对象的属性指向构造函数的属性执行构造函数,将的指向前端面试题及答案浏览器篇前端面试题及答案篇前端面试题及答案篇前端面试题及答案性能优化篇 这篇文章并不是最全的前端面试题(没有最全,只有更全),只是针对自己面试过程中遇到的一些难题、容易忽略的题做一个简单的笔记,方便后面有面试需要的小伙伴们借鉴,后续内容会不定时更新,有错误之处希望大家不吝指出...

    Shimmer 评论0 收藏0
  • JavaScript系列--浅析JavaScript解析赋值、浅拷贝和深拷贝的区别

    摘要:它将返回目标对象。有些文章说是深拷贝,其实这是不正确的。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。使用深拷贝的场景完全改变变量之后对没有任何影响,这就是深拷贝的魔力。 一、赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为: 1、基本数据类型:赋值,赋值之后两个变量互不影响 2、引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有...

    laznrbfe 评论0 收藏0
  • JavaScript 深拷贝

    摘要:深拷贝是一件看起来很简单的事情,但其实一点儿也不简单。我们也可以利用这个实现对象的深拷贝。而是利用之前已经拷贝好的值。深拷贝的详细的源码可以在这里查看。大功告成我们虽然的确解决了深拷贝的大部分问题。 js深拷贝是一件看起来很简单的事情,但其实一点儿也不简单。对于循环引用的问题还有一些内置数据类型的拷贝,如Map, Set, RegExp, Date, ArrayBuffer 和其他内置...

    zhangwang 评论0 收藏0
  • js深浅复制

    摘要:总结综上所述,数组的深拷贝比较简单,方法没有什么争议,对象的深拷贝,比较好的方法是用的方法实现,或者递归实现,比较简单的深复制可以使用实现参考资料知乎中的深拷贝和浅拷贝深入剖析的深复制 深浅复制对比 因为JavaScript存储对象都是存地址的,所以浅复制会导致 obj 和obj1 指向同一块内存地址。我的理解是,这有点类似数据双向绑定,改变了其中一方的内容,都是在原来的内存基础上做...

    Apollo 评论0 收藏0

发表评论

0条评论

chemzqm

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<