JS浅拷贝导致的聊天信息丢失问题

薄洪涛4年前JS1001

问题背景

我们做了一个基于vue的H5在线问诊项目,集成了融云IM,每次进入到会话页面的时候,需要恢复历史聊天数据,我们的历史聊天数据做了三层缓存,内存-->浏览器缓存(LocalStorage)----> 服务器数据库(每次收发信息融云服务器会转发到我们的服务器然后存到数据库),并且我们开发了获取聊天记录的接口,进入到页面的时候,我们的恢复历史聊天数据的部分策略如下:

  1. 链接融云,若融云推送未读消息或者本地localstorage中有此会话数据,存内存

  2. 在1.5s之后,判断加载到内存中的数据,若内存数据为空或者内存中的数据不是当前会话的聊天数据,请求接口获取数据

问题在这里,当我判断内存中的数据是否是当前会话的聊天数据时,代码是这么写的

// that.$store.state.answer-->表示内存中的历史聊天数据
let messageHistory = that.$store.state.answer 
if (messageHistory.length === 0) {
  that.$store.state.answer = []
  that.getChatRecord()
} else if (messageHistory.length > 0) {
  let tmp = messageHistory.pop()
  if (tmp.targetId.toString() !== that.targetId) {
    console.log('内存中数据不是当前会话的数据丢弃')
    that.$store.state.answer = []
    that.getChatRecord()
  } else {
    console.log('无需加载')
  }

此代码执行之后,我发现会丢消息!!!

明明localstorage中有10条消息,内存读出来之后就会变成9条,少一条,大佬们可能一眼看出来问题所在,没错就是下面这行代码的原因

let tmp = messageHistory.pop()

这里的messageHistory实际上和that.$store.state.answer 都指向同一块内存地址,对messageHistory做了pop操作,也就是内存中的数据删除了一条,所以会丢消息

我们知道,js的数据类型Number,string,undefined,null,bool,object 六种,对前五种,都是在内存中直接存储数据的,所以对象复制相当于新开辟一块内存空间,所以深拷贝和浅拷贝没有区别, 如果数据类型是Object,他在数据空间中真正存储的一个地址,数据都是存储在另外一个地方,通过存储的那个地址可以访问,所以对象的简单拷贝就只是仅仅在新开辟的数据空间中复制了那个地址,所以如果对原来对象里面的数进行改变,新的对象也会受到影响。

深拷贝Object的方法如下:

  1. ES6 的扩展运算符(…)

  2. Object.assign()

  3. JSON.parse(JSON.stringify(obj))

QQ20201119-232117@2x.png

特别注意:

第一,二方法无法拷贝多重嵌套

第三种方法针对undefined, function , regExg是无法拷贝的

因此我们实现一个递归拷贝方法

// 定义一个深拷贝函数  接收目标target参数function deepClone(target) {
    // 定义一个变量
    let result;
    // 如果当前需要深拷贝的是一个对象的话
    if (typeof target === 'object') {
    // 如果是一个数组的话
        if (Array.isArray(target)) {
            result = []; // 将result赋值为一个数组,并且执行遍历
            for (let i in target) {
                // 递归克隆数组中的每一项
                result.push(deepClone(target[i]))
            }
         // 判断如果当前的值是null的话;直接赋值为null
        } else if(target===null) {
            result = null;
         // 判断如果当前的值是一个RegExp对象的话,直接赋值    
        } else if(target.constructor===RegExp){
            result = target;
        }else {
         // 否则是普通对象,直接for in循环,递归赋值对象的所有值
            result = {};
            for (let i in target) {
                result[i] = deepClone(target[i]);
            }
        }
     // 如果不是对象的话,就是基本数据类型,那么直接赋值
    } else {
        result = target;
    }
     // 返回最终结果
    return result;}

ok,到此结束

休息休息睡觉睡觉,晚安大佬们

IMG_3420.jpg

标签: JS深拷贝

相关文章

js复制Dom节点内容到系统剪切板

需求:密码生成器,生成后比较长的密码串,点击复制按钮,实现复制到系统剪切板;这里使用到一款js插件:可以实现一键复制 https://github.com/zenorocha/clipboard.js...

这不是真的ES6之ECMAScript 6

这不是真的ES6之ECMAScript 6

最新想学习es6(elasticsearch6),在我的破服务器(512m内存)上搭建了java虚拟机(JVM)环境,首先说明一点,买服务器还是要买高配呀,如图当我安装elasticsearch看到内...

JsonP跨域请求详解

JsonP跨域请求详解

先来了解一下基本的概念,什么叫做跨域?举个例子,在localhost:8080上有一个ajax请求,代码如下<script src="js/jquery-1.7.2.js&q...

vue搭建基于融云的聊天室

    最近项目比较忙,在做完了一个在线问诊的项目后,想给大家分享下其中用到的,但是百度上资料又比较少的技术    需求...

vue2.0搭建vue脚手架

vue2.0搭建vue脚手架

最近有个项目,老大评估后想用前后端分离技术来做,借这个机会来熟悉下vue前端框架,这次就搭建一个vue项目,下一个目标就是把vue和yii2.0整合;开搞搭建node环境这步很简单,下载安装包右键安装...

node学习及小爬虫的实现

node学习及小爬虫的实现

菜鸟一只,抽空了解了下node,然后总结下学到的重点;首先是安装nodejs,需要安装的版本最好是v6.0以上,不然有些特性不兼容nmp是和nodeJS一起安装的一个管理包工具,类似于python的p...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。