浅拷贝与深拷贝

145 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文作为本人学习总结之用,以笔记为主,同时分享给大家.
因为个人技术有限,如果有发现错误或存在疑问之处,欢迎指出或指点!不胜感谢!

对象/数组拷贝

区别浅拷贝与深拷贝

  • 纯语言表达:

    • 浅拷贝: 只是复制了对象属性或数组元素本身(只是引用地址值)
    • 深拷贝: 不仅复制了对象属性或数组元素本身, 还复制了指向的对象(使用递归)
  • 举例说明: 拷贝persons数组(多个人对象的数组)

    • 浅拷贝: 只是拷贝了每个person对象的引用地址值, 每个person对象只有一份
    • 深拷贝: 每个person对象也被复制了一份新的

实现浅拷贝

实现浅拷贝

  • 方法一: 利用ES6语法
  • 方法二: 利用ES5语法: for...in
/* 

*/
/* 方法一: 利用ES6语法*/
function clone1(target) {
  // 如果是对象(不是函数, 也就是可能是object对象或者数组)
  if (target!=null && typeof target==='object') {
    if (target instanceof Array) {
      // return target.slice()
      // return target.filter(() => true)
      // return target.map(item => item)
      return [...target]
    } else {
      // return Object.assign({}, target)
      return {...target}
    } 
  }
  // 基本类型或者函数, 直接返回
  return target
}

/* 方法二: 利用ES5语法: for...in */
function clone2(target) {
  if (target!=null && typeof target==='object') {
    const cloneTarget = Array.isArray(target) ? [] : {}
    for (let key in target) {
      if (target.hasOwnProperty(key)) {
        cloneTarget[key] = target[key]
      }
    }
    return cloneTarget
  } else {
    return target
  }
}

实现深拷贝

  • 实现一: 大众乞丐版

    • 问题1: 函数属性会丢失
    • 问题2: 循环引用会出错
  • 实现二: 面试基础版

    • 解决问题1: 函数属性还没丢失
  • 实现三: 面试加强版本

    • 解决问题2: 循环引用正常
  • 实现四: 面试加强版本2(优化遍历性能)

    • 数组: while | for | forEach() 优于 for-in | keys()&forEach()

    • 对象: for-in 与 keys()&forEach() 差不多

/* 
1). 大众乞丐版
    问题1: 函数属性会丢失
    问题2: 循环引用会出错
*/
function deepClone1(target) {
  return JSON.parse(JSON.stringify(target))
}

/* 
2). 面试基础版本
    解决问题1: 函数属性还没丢失
*/
function deepClone2 (target) {
  if (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
  }

  return target
}

/* 
3). 面试加强版本
    解决问题2: 循环引用正常
*/
function deepClone3 (target, map=new Map()) {
  if (target!==null && typeof target==='object') {
    // 从缓存容器中读取克隆对象
    let cloneTarget = map.get(target)
    // 如果存在, 返回前面缓存的克隆对象
    if (cloneTarget) {
      return cloneTarget
    }
    // 创建克隆对象(可能是{}或者[])  
    cloneTarget = target instanceof Array ? [] : {}
    // 缓存到map中
    map.set(target, cloneTarget)

    for (const key in target) {
      if (target.hasOwnProperty(key)) {
        // 递归调用, 深度克隆对象, 且传入缓存容器map
        cloneTarget[key] = deepClone3(target[key], map)
      }
    }

    return cloneTarget
  }

  return target
}

/* 
4). 面试加强版本2(优化遍历性能)
    数组: while | for | forEach() 优于 for-in | keys()&forEach() 
    对象: for-in 与 keys()&forEach() 差不多
*/
function deepClone4 (target, map=new Map()) {
  if (target!==null && typeof target==='object') {
    // 从缓存容器中读取克隆对象
    let cloneTarget = map.get(target)
    // 如果存在, 返回前面缓存的克隆对象
    if (cloneTarget) {
      return cloneTarget
    }
    // 创建克隆对象(可能是{}或者[])  
    if (target instanceof Array) {
      cloneTarget = []
      // 缓存到map中
      map.set(target, cloneTarget)
      target.forEach((item, index) => {
        cloneTarget[index] = deepClone4(item, map)
      })
    } else {
      cloneTarget = {}
      // 缓存到map中
      map.set(target, cloneTarget)
      Object.keys(target).forEach(key => {
        cloneTarget[key] = deepClone4(target[key], map)
      })
    }

    return cloneTarget
  }

  return target
}