JavaScript技巧: 实现深、浅拷贝

42 阅读2分钟

JavsScript 变量包含两种不同数据类型的值: 基本数据类型引用数据类型,基本数据类型: 标识符-值 存储在栈内存中,引用数据类型: 标识符存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值

浅拷贝

拷贝一层,引用类型数据任然拷贝的是地址的引用

// 1、浅克隆引用对象(利用 es6 扩展运算符),基础类型直接返回
function clone1(target) {
  if (typeof target === 'object' && target !== null) {
    // 利用 es6 扩展运算符返回容器对象
    if (Array.isArray(target)) {
      return [...target]
    } else {
      return { ...target }
    }
  } else {
    return target
  }
}

// 2、浅克隆引用对象,返回容器对象,类型类型直接返回
function clone2(target) {
  if (typeof target === 'object' && target !== null) {
    const result = Array.isArray(target) ? [] : {}
    for (const key in target) {
      // 判断数据上是否有 key 这个属性
      if (target.hasOwnProperty(key)) {
        // 将属性压入到 result 容器中
        result[key] = target[key]
      }
    }
    return result
  } else {
    return target
  }
}

深拷贝

递归拷贝多层,将引用数据类型解析到基础类型时再进行拷贝

/**
 * * 深拷贝(乞丐版),利用 JSON 将数据变成 JSON 格式再根据 JSON 格式创建 JS 数据完成拷贝
 * ! 缺点:1、无法拷贝方法;2、无法解决循环引用
 *
 * @param {*} target
 * @return {*}
 */
function deepJsonClone(target) {
  // 使用 JS 数据创建 JSON 数据
  let str = JSON.stringify(target)
  // 使用 JSON 格式数据创建 JS 数据
  let data = JSON.parse(str)
  return data
  // 简写 return JSON.parse(JSON.stringify(target))
}

/**
 * * 深拷贝(完整版),在原有基础上提升性能
 * ? 数组:while | for | forEach() 优于 for-in | keys()&forEach()
 * ? 对象:for-in 与 keys()&forEach() 差不多
 *
 * @param {*} target
 * @return {*}
 */
function deepClone(target, map = new Map()) {
  // 判断是否为引用数据类型
  if (typeof target === 'object' && target !== null) {
    // 判断值是否已被拷贝,如是,则直接返回此值
    let cache = map.get(target)
    if (cache) {
      return cache
    }

    let isArray = Array.isArray(target)
    const result = isArray ? [] : {}
    // 创建 map 数据容器以存放用于判断的值
    map.set(target, result)
    if (isArray) {
      target.forEach((item, index) => {
        result[index] = deepClone(item, map)
      })
    } else {
      Object.keys(target).forEach((key) => {
        result[key] = deepClone(target[key], map)
      })
    }
    return result
  } else {
    return target
  }
}