浅拷贝与深拷贝

85 阅读2分钟

1. 数据存储类型

javaSript 中存在两种数据类型

  • 基本类型
  • 引用类型

基本类型保存在栈内存

引用类型的数据保存在堆内存中,引用类型的变量是一个指向堆内存中实际对象的引用地址,存在栈中

2. 浅拷贝

浅拷贝是指创建新的数据,这个数据有着原始数据属性值的一份精准拷贝

如果属性是基本类型,拷贝的是基本类型的值;如果属性是引用类型,拷贝的是内存地址;

浅拷贝

function shallowCoapy(obj) {
  const newObj = {};
  for (const k in obj) {
    if (Object.hasOwnProperty.call(obj, k)) {
      newObj[k] = obj[k];
    }
  }
  return newObj;
}

其他浅拷贝方法

  • Object.assgin
  • 扩展运算符

Object.assgin

const objA = {
  age: 18,
}

const objB = Object.assign({}, objA);

扩展运算符

const objA = {
  age: 18
}

const objB = {
  ...objA
}

3. 深拷贝

深拷贝创建一个新的栈,递归复制对象的所有层,使得拷贝和原始对象没有引用类型的共享

常见的深拷贝方式

  • lodash.cloneDeep()
  • jQuery.extend()
  • JSON.stringfy()
  • 手写递归循环

lodash.cloneDeep()

import { cloneDeep } from 'lodash';

const objA = {
  age: 18,
  score: {
    math: 90,
    music: 80
  },
};

const objB = cloneDeep(objA);

jQuery.extend()

import $ from 'jQuery';

const objA = {
  age: 18,
  score: {
    math: 90,
    music: 80
  },
};

const objB = $.extend(objA);

JSON.stringfy()

const objA = {
  age: 18,
  score: {
    math: 90,
    music: 80
  },
};

const objB = JSON.parse(JSON.stringify(objA));

缺点:会忽略一些特殊对象(MapSetDateRegExp

手写递归

function deepClone(obj, hash = new WeakMap()) {
  // 如果是 null 或者不是对象和数组,直接返回
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // 如果是日期类型,使用新的日期对象
  if (obj instanceof Date) {
    return new Date(obj);
  }

  // 如果是正则表达式对象,使用新的正则表达式对象
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }

  // 如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) {
    return hash.get(obj);
  }

  // 如果是对象或数组,递归调用
  let cloneObj = new obj.constructor; // 继承原型链
  hash.set(obj, cloneObj);
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  
  return cloneObj;
}

// 使用示例
const original = {
  number: 1,
  bool: false,
  string: 'string',
  date: new Date(),
  undefined: undefined,
  null: null,
  array: [0, 1, 2],
  object: {
    child: { num: 1 }
  },
  regexp: new RegExp(/regexp/i)
};

const copied = deepClone(original);
console.log(copied);

总结

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个内存地址

  • 深拷贝是通过递归将所有层次进行拷贝,最终属性为对象的会指向不同的地址