JavaScript之深浅拷贝

176 阅读2分钟
  • 基本数据类型和引用数据类型
  • 深拷贝和浅拷贝

基本类型 & 引用类型

  • 基本数据类型:undefinednullBooleanStringNumberSymbol
  • 引用数据类型:ObjectArrayDateFunctionRegRxp等。

存储方式:

  • 基本数据类型:基本数据类型在内存中分别占有固定大小空间,通过按值来访问
  • 引用数据类型:引用数据类型保存在堆内存中,因为值的大小不固定,但是内存地址大小是固定的,所以在栈内存中存放的是该对象的访问地址(以及对象的变量标识符)。当查询引用类型时,先从栈中读取内存地址,再通过地址找到堆中的值,所以叫做按引用访问

在计算机的数据结构中,栈比堆的运算速度快,Object、Array是一种复杂的结构且都可以增删改查。将他们放在堆中是为了不影响栈的效率。

深拷贝 & 浅拷贝

  • 浅拷贝:仅仅拷贝了引用地址,对象的改变会互相影响。
  • 深拷贝:深拷贝会拷贝所有的属性,在堆中重新分配内存,互不影响。

使用场景:

浅拷贝:

  • Object.assign()
    将所有可枚举属性的值从一个或者多个源对象复制到目标对象,同时返回目标对象。
  • {...obj} 扩展运算符
  • Array.prototype.slice()Array.prototype.concat()

深拷贝:

  • JSON.parse(JSON.stringify(obj))

先把一个对象序列化为一个JSON字符串,再反序列化为一个对象

问题:

  1. 会忽略undefinedsymbolfunction
  2. 不能解决循环引用的对象
  3. 不能正确处理new Date()
  4. 不能处理正则
  • jQuery.extend()
  • lodash.cloneDeop()
  • 深拷贝可以使用递归+浅拷贝实现,浅拷贝时判断属性是否是对象,如果是对象就进行递归操作。
function deepCopy(obj) {
    if (typeof obj !== 'object') {
        throw new TypeError('类型错误');
    }

    const targetObj = Array.isArray(obj) ? [] : {};

    for (let key in obj) {
        if(obj.hasOwnProperty(key)) {
            if(obj[key] && typeof obj[key] === 'object') {
                targetObj[key] = deepCopy(obj[key]);
            }else {
                targetObj[key] = obj[key];
            }
        }
    }
}