资讯专栏INFORMATION COLUMN

没有对象?那就复制一个吧!(科普向,实际应用向)

amuqiao / 3118人阅读

摘要:基本数据类型将变量和值一起放在栈内存引用数据类型则将变量放在栈内存而将值放在堆内存。该怎么理解没图我说个假设有个变量在内存中是这样的栈内存中的变量指向堆内存中一块内存相当于持有该内存的指针,而那块内存中存储变量的相关内容。

趁着周五没那么忙,抽个空整理一下最近使用比较频繁的一个小技术 对象的深复制
感觉啊,这个标题和今天的节日(假装不知道原来是情人节)那么遥相呼应。啊,没有女朋友?没有女朋友没关系,复制一个啊。先走你一个:console.log("GirlFriend")
同时笔者需要预个警,本篇注重实际中的应用情况,中规中矩,不搞花里胡哨。所以主要是对数组字面量对象进行讨论,毕竟前端开发中遇到最多的对象就是这两个了。在正式分析对象的深复制这个问题之前,笔者觉得有必要对在座的朋友科普下必要的知识。

科普区
JavaScript中数据类型分为哪几种?

这个问题在面试中的命中率真的挺高,而且也是本篇的重要所在。了解的朋友可跳过继续向下看,不了解的朋友请跟着我慢慢理解。
JavaScript中数据类型分为基本数据类型引用数据类型 (当然还有别的叫法,只是笔者认为这种叫法更科学)。

基本数据类型: null, undefined, int, string, boolean

引用数据类型: {"a":"b"}(字面量对象), Array, Map, Set, Symbol 等等...其实就是基本数据类型以外的类型

而且,不同的数据类型在内存中的存储方式也是不同的:

基本数据类型在内存中的存储方式

我们都知道,JavaScript中的内存分为栈内存堆内存(不知道?请点击)。基本数据类型将变量和值一起放在栈内存;引用数据类型则将变量放在栈内存而将值放在堆内存。先来讲基本数据类型
基本数据类型在JavaScript编程中用得极为广泛,比如:

let a = 1;
let b = "name";
let c = true;
let d = null;
let e = undefined;

这些都属于基本数据类型,都存储在栈内存中;如以下形式:

栈内存中的变量数据有个好处是如果需要获取某个变量值的copy值很方便,直接通过 = 操作即可,不需要考虑额外的问题。比如:let f = a; 这个操作意在拷贝一份 a 这个变量,此时栈内存中就变成这样了:

多出一个变量 f,即使说是从 a 复制而来的,结果却和 a 没有任何关系。用代码验证一下:

let a = 1;
let f = a;

//此时改变a的值
a = 2;

console.log(`a 是${a}`);
console.log(`f 是${f}`);

运行结果:

引用数据类型在内存中的存储方式

上面说到:引用数据类型则将变量放在栈内存而将值放在堆内存。该怎么理解?没图我说个jb?
假设有个变量person:

let person={
    "name":"Mario"
}

在内存中是这样的

栈内存中的变量 person 指向堆内存中一块内存(相当于持有该内存的指针),而那块内存中存储 person 变量的相关内容。
因此可以看出引用数据类型的复制并没有基本数据类型来得方便。即便如此,我们还是来证实下这个想法:

let person={
    "name":"Mario"
}

let person_copy = person;

//修改person中的内容
person["name"]="JavaScript";

console.log(`person: ${person["name"]}`);
console.log(`person_copy: ${person_copy["name"]}`);

运行结果:

可以看出当我们复制好一份 person_copy ,并对 person 进行了一次修改。结果两个变量同时变化。为什么?
因为当程序进行到let person_copy = person;这里的时候,并不是直接把 person 的内容赋值给变量 person_copy,而是把 person 的指针赋值给了变量 person_copy。

所以说变量 person 和 person_copy 都持有了该内存块的指针,因此不管哪个变量修改了内存中的内容,另一个变量对应的值也会变化。最终我们可以确定:如果想复制一个引用类型的数据,就是要将该内存块中的内容复制进另一个内存块中并把新的指针赋值给新变量

大概内容已经科普完了,接下来就开始本文的重点内容

对象的(深)复制

在开发过程中,如果某条数据(尤其是从后台请求回来的数据)使用频率很高而且用途复杂,那么就不得不为它进行一次复制以备不时之需。针对使用频率较高的两个对象数组字面量对象,我们开始逐一讨论。

数组的复制

数组在实际开发中,元素主要分为基本数据类型字面量对象(不排除还有别的类型数据,只是笔者没遇到过,所以只针对普遍的情况)。
针对元素是基本数据类型的数组的复制操作,笔者提供4种方法:

ES6的对象扩展运算符 [...]

let origin = [1, 2, 3, 4, 5];
let another = [...origin];

//向原数组中添加一个元素
origin.push(6);
console.log(`another元素: ${another}`);

运算结果:

slice

let origin = [1, 2, 3, 4, 5];
let another = origin.slice();

//向原数组中添加一个元素
origin.push(6);
console.log(`another元素: ${another}`);

运行结果:

concat

let origin = [1, 2, 3, 4, 5];

//相当于向origin中拼接一个空数组
let another = origin.concat([]);

//向原数组中添加一个元素
origin.push(6);
console.log(`another元素: ${another}`);

运行结果:

JSON.stringify

这个方法可行但是几乎没人这么用。原理是将数组转为字符串再转回数组类型。

let origin = [1, 2, 3, 4, 5];
let another = JSON.parse(JSON.stringify(origin));

//向原数组中添加一个元素
origin.push(6);
console.log(`another元素: ${another}`);

运行结果:

其中笔者觉得第一和第二个方法比较好用。不过如果数组中的元素是字面量对象的话,请继续向下看。

字面量对象的深复制

实际开发中,所谓字面量对象可以人为是一段json数据。json的重要性不用说,那么相当重要,前后端交互的核心。
先给出一段json:

{
    "name": "Mario",
    "age": 26,
    "isCoder": true,
    "homeWebPage": null,
    "fullStackSkills": undefined,
    "hobbies": ["LoL", "Travel", "Coding"],
    "phone": {
        "home": 123321,
        "office": 456654
    }
}

首先分析这段json中的数据,不管是基本数据类型还是引用数据类型都有了,所以想要复制这个json对象,我们需要针对不同的数据做不同的处理。根据科普的知识我们已经知道,复制基本数据类型可以直接赋值,对于引用数据类型则不行,而且主要使用到的数组和字面量对象(这里也可以认为是json)这两个类型数据的复制方法都不同。因此我们首先要判断某一个数值是否是对象:

//判断item是否为"object";该方法主要是为了区分参数是基本类型还是引用类型
function isObject(item) {
    return (item === null || item === undefined) ? false : (typeof item === "object");
}

其次,我们可以看到origin是一段json,orgin中的phone字段对应的值也是一段json,所以要想整个复制这个对象难免要用到递归,一层一层嵌套进行。奉上核心代码:

/**
 * 
 * @param {字面量对象} origin 
 * @param {origin的镜像对象} mirror
 */
function deepClone(origin, mirror) {
    //获取该字面量对象的所有的key
    let keys = Object.keys(origin);
    //遍历所有的key已保证复制的完整
    keys.forEach(key => {
        let value = origin[key];
        if (isObject(value)) {   //判断是否为对象,如果是则需要额外处理;如果不是则直接复制
            if (Array.isArray(value)) { //判断是否为数组,如果是则需要复制该数组并存入mirror;如果不是则进行递归调用
                let copy = value.slice();
                mirror[key] = copy;
            } else {
                //初始化本次字面量对象的镜像对象
                let obj = {};
                mirror[key] = obj;
                //引用传值
                //递归调用
                deepClone(value, obj);
            }
        } else {
            mirror[key] = value;
        }
    });
}

通过测试,

//Test
let mirror = {};
deepClone(origin, mirror);

//向原对象中的hobbies中增加一项
origin["hobbies"].push("Eat");
console.log(mirror);

运行结果:

证明方法有效。
当然针对json数据的复制,也可以只用JSON.parse(JSON.stringify(origin))实现,具体效率怎么样,笔者也没有进行测试。所以这块有待验证。因为该文章注重实际开发中的应用,所以例子没有用到复杂的对象(例如:Set, Symbol 等等...)。所以如果有这个疑问的朋友也不用纠结了。

写得差不多了,能想到了就这些了...收拾收拾准备跑路了

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108309.html

相关文章

  • 我对分布式一致性协议的学习心得 - CAP、BASE、NWR

    摘要:当发生网络分区时,你将面临两个选择如果坚持保持各节点之间的数据一致性选择,你需要等待网络分区恢复后,将数据复制完成,才可以向外部提供服务。期间发生网络分区将不能对外提供服务,因为它保证不了数据一致性。则强调是高可用,对数据一致性要求更低。这篇文章着重点不在于科普,毕竟关于CAP、BASE的理论的文章,网上很多。所以本文科普篇幅尽量小(只包含概念描述)。主要从几个侧面的问题来描述CAP,进而描...

    Tecode 评论0 收藏0
  • JWT(Json Web Token) 科普

    摘要:部分是对前两部分的签名,防止数据篡改。也就是说,一旦签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。为了减少盗用,的有效期应该设置得比较短。为了减少盗用,不应该使用协议明码传输,要使用协议传输。 JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理和用法。 showImg(https://www.wangbase.com/blogimg...

    SKYZACK 评论0 收藏0
  • 科普丨到底什么是Content Delivery Network云分发网络CDN?

    摘要:而,是部分内容的缓存,智能程度更高。用户向缓存服务器发起请求,缓存服务器响应用户请求,将用户所需内容传送到用户终端。内容进行分发后,源服务器的被隐藏,受到攻击的概率会大幅下降。由一个核心云计算中心,对所有终端节点提供服务。如今这个移动互联网时代,越来越多的人使用手机观看视频,丰富自己的娱乐生活。可是,大家在追剧的时候,有没有想过一个问题——为什么有时候明明自己的网速很快,但观看视频时,仍然卡...

    Tecode 评论0 收藏0
  • GraphQL 科普 前端

    摘要:样例前端传入字段和结构。后台按照前端的需求返回数据。则将前后台通信直接分为两大类和。顾名思义,是默认的操作符,代表查询,是不会给服务端带来副作用的请求。文档文档部分文档就是前端向后台描述所需的字段。降低前后端沟通成本。 简介 showImg(https://segmentfault.com/img/bVbmKX5?w=150&h=150); GraphQL是基于「类型系统」来执行查询的...

    Bmob 评论0 收藏0
  • 科普跨站平台 XSS shell 使用方法

    摘要:例如这说明在应用程序的消息中响应。这意味着应用程序从中提取信息对其进行处理,并显示给用户。配置使用服务器。根据请求,服务器建立一个通道与受害人进行交互。受害者受害者部分显示了受害者的名单。 理解xss shell是什么之前,让我们一起回顾一下一些基本的xss(跨站脚本),xss是最常见的一个漏洞,存在于今天许多的web应用程序。xss是攻击者试图通过web应用程序执行恶意脚本的技术,攻...

    fanux 评论0 收藏0

发表评论

0条评论

amuqiao

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<