js引用類型深拷貝、淺拷貝方法封裝

引用類型的深拷貝、淺拷貝在前端領域一直是個很重要的知識點,不只在業務中頻繁使用,也是面試官們喜歡考的的知識點之一。本篇將封裝引用類型的深拷貝、淺拷貝方法,並解決在封裝過程當中出現的問題。前端

1、淺拷貝面試

淺拷貝通常比較簡單,缺點也很明顯,引用類型的屬性並非真正的拷貝,而是拷貝的引用地址,改變一個當中的屬性值,另外一個也跟着變化。json

方法封裝:數組

/**
 * 淺拷貝
 * @param {*} target 
 * @returns 
 */
export function clone (target) {
  if (target instanceof Array) {
    // return [...target]
    // return target.slice()
    // return [].concat(target)
    // return Array.from(target)
    // return target.filter(value => true)
    // return target.map(item => item)
    return target.reduce((pre, item) => {
      pre.push(item)
      return pre
    }, [])
  } else if (target!==null && typeof target==='object') {
    return {...target}
  } else {// 若是不是數組或對象, 直接返回
    return target
  }
}

 

 爲了解決淺拷貝的痛點,因而就有了深拷貝,能夠拷貝引用類型的屬性,且互相獨立,互不影響。緩存

 

 2、深拷貝(3種方法)函數

  • JSON.parse(JSON.stringify(obj))
  • 遞歸
  • 遞歸+緩存池

1.JSON.parse(JSON.stringify(obj)):JSON.stringify把對象轉成字符串,再用JSON.parse把字符串轉成新的對象spa

export function deepClone1 (target) {
  return JSON.parse(JSON.stringify(target))
}

這種深拷貝的方式我平時業務中也常常使用,但使用的前提是對拷貝的對象屬性很是清楚,屬性中不能包含函數,由於function沒辦法轉化爲json格式的對象,因此函數屬性會丟失!code

還有一個缺陷就是,若是拷貝的對象屬性中包含本身(循環引用),在深拷貝的時候會陷入死循環。對象

 

 

 深拷貝後:blog

2.利用遞歸來進行深拷貝

export function deepClone2 (target) {
  // 被處理的目標是數組/對象
  if (target instanceof Array || (target!==null && typeof target==='object')) {
    const cloneTarget = target instanceof Array ? [] : {}
    for (const key in target) {
      if (target.hasOwnProperty(key)) {
        cloneTarget[key] = deepClone2(target[key])  // 對屬性值進行遞歸處理
      }
    }
    return cloneTarget
  } else {
    return target
  }
}

打印結果1:解決函數屬性丟失

 

打印結果2:死循環問題沒有解決

 

 

 3.利用「緩存池」來解決循環引用形成的死循環

export function deepClone3 (target, map=new Map()) {
  // 被處理的目標是數組/對象
  if (target instanceof Array || (target!==null && typeof target==='object')) {
    // map中存在對應的克隆對象, 造成一個緩存池,若是當前對象存在,則返回
    let cloneTarget = map.get(target)
    if (cloneTarget) {
      return cloneTarget // 不要對同一個對象進行屢次clone
    }
    // 建立克隆對象
    if (target instanceof Array) {
      cloneTarget = []
      // 保存到map容器
      map.set(target, cloneTarget)
      // 向數組添加元素
      target.forEach((item, index) => {
        cloneTarget[index] = deepClone(item, map)
      })
    } else {
      cloneTarget = {}
      // 保存到map容器
      map.set(target, cloneTarget)
      // 向對象添加屬性
      for (const key in target) {
        if (target.hasOwnProperty(key)) {
          cloneTarget[key] = deepClone(target[key], map)  // 對屬性值進行遞歸處理
        }
      }
    }
    
    return cloneTarget
  } else {
    return target
  }
}

打印結果:

 

 利用緩存池來解決引用類型的循環引用問題,每一個引用類型只拷貝一次,這樣就不會陷入死循環了。

以上就是我對js中引用類型深拷貝和淺拷貝的理解和封裝了,若是你有更好的方案歡迎留言指教!

 

腳踏實地行,海闊天空飛

相關文章
相關標籤/搜索