Javascript 各种数据类型深拷贝

一. 基础知识

1.1 数据类型
javascript有几种数据类型。这是一个很简单的问题,但是也是面试官喜欢问的致命问题,因为基本上答错一个就印象分大打折扣了。在Javascript中一共只有以下7种数据类型。

Number
String
Boolean
Null
Undefined
Symbol
Object

其中前面6种类型是原始数据类型,而Object是引用数据类型。我更喜欢把前面6种称之为简单数据类型,而把Object称之为复杂数据类型。因为简单数据类型没有子类型了,不可以再进行拆分了,而复杂数据类型还有子类型,比如Array,Function,RegExp,Date等对象,正是因为这些子类型的不同导致了深拷贝的各种问题。这就是为什么很多人在回答有哪些基本数据类型时会把Array和Function答进去。事实上他们只是Object的子类型,并不是基本数据类型。数据类型的不同,会导致在内存中的存储方式的不同,如果是简单数据类型,存储在栈空间中,存储的是一个值;如果是复杂数据类型,存储在堆空间中,存储的是一个引用。正是这种存储方式的差异,导致了浅拷贝和深拷贝的区别。

在这里插入图片描述
1.2 浅拷贝和深拷贝

我们先来明确一下到底什么是浅拷贝什么是深拷贝。

浅拷贝: 如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以修改新拷贝的对象会影响原对象。

let obj = {
    id:1,
    info:{
        name:"hello",
        age:24
    }
}
let obj2 = obj; // 赋值就是一个浅拷贝
obj2.id = 3;
console.log(obj.id);   // 3

深拷贝: 将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

let obj = {
    id:1,
    info:{
        name:"hello",
        age:24
    }
}
let obj2 = JSON.parse(JSON.stringify(obj)); // 这里实现深拷贝  暂时记住就好
obj2.id = 3;
obj2.info.name = "刘亦菲";
console.log(obj.id);   // 1
console.log(obj.info.name);   // hello

上面的代码中obj2是通过深拷贝obj1得到的,修改obj2的属性,发现Obj1的属性不会跟着修改。这是深拷贝。

二、深拷贝的实现

在上面的深拷贝的代码示例中,我使用了 JSON.parse(JSON.stringify()) 实现了一个深拷贝。这就是日常开发中使用较为频繁的一个深拷贝方法,它可以实现一些不是那么复杂的数据类型的深拷贝。示例:

let num = 24;
let bool = true;
let obj = {
  id:1
  info:{
    name:"hello",
    age:24
  }
}

let num1 = JSON.parse(JSON.stringify(num))// num1就是num的深拷贝   虽然简单的数据类型这种拷贝没啥意义
let bool1 = JSON.parse(JSON.stringify(bool))// num1就是num的深拷贝   虽然简单的数据类型这种拷贝没啥意义
let obj2 = JSON.parse(JSON.stringify(obj))// 复杂数据类型也可以使用JSON.parse(JSON.stringify(obj))

但是这种方法存在一些缺点,由于它是依赖于JSON,因此它不支持JSON不支持的其他格式,通过JSON的官网可知,JSON只支持object,array,string,number,true,false,null这几种数据或者值,其他的比如函数,undefined,Date,RegExp等数据类型都不支持。对于它不支持的数据都会直接忽略该属性。

1. 对象中不能有函数,否则无法序列化

在这里插入图片描述

2. 对象中不能有undefined,否则无法序列化

在这里插入图片描述
3. 对象中不能有RegExp正则,否则无法序列化

在这里插入图片描述
4. Date类型数据会被转化为字符串类型

如果对象中存在Date类型的数据,会被转换成字符串,从而丢失Date的一些特性,比如时间格式化等方法。
在这里插入图片描述
5. 对象不能是环状结构的,否则会导致报错

所谓环状结构的对象,就是对象的属性又指向了自身,window就是最常见的一个环状对象。

let obj = {name:'hello'}
obj.self = obj   // self属性又指向了obj对象,形成了一个换

这种环状结构的对象,在使用JSON.parse(JSON.stringify)深拷贝时会报错。

在这里插入图片描述
小结:从上面的分析中,我们可以看到 JSON.parse(JSON.stringify()) 虽然能够深拷贝一个对象,但是存在很大的局限性,对于复杂的对象就不适用了。因此,我们需要采用另外的方式来实现深拷贝,也就是通过递归的方式手动实现深拷贝。

三、 终极大法(拿来即用)

较为完整的深拷贝。最终的实现函数如下

function deepClone(target,cache = new Map()){
  if(cache.get(target)){
      return cache.get(target)
  }
  if(target instanceof Object){
      let dist ;
      if(target instanceof Array){
        // 拷贝数组
        dist = [];
      }else if(target instanceof Function){
        // 拷贝函数
        dist = function () {
          return target.call(this, ...arguments);
        };
      }else if(target instanceof RegExp){
        // 拷贝正则表达式
       dist = new RegExp(target.source,target.flags);
      }else if(target instanceof Date){
          dist = new Date(target);
      }else{
        // 拷贝普通对象
        dist = {};
      }
      // 将属性和拷贝后的值作为一个map
      cache.set(target, dist);
      for(let key in target){
          // 过滤掉原型身上的属性
        if (target.hasOwnProperty(key)) {
            dist[key] = deepClone(target[key], cache);
        }
      }
      return dist;
  }else{
      return target;
  }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值