前端开发系列120-进阶篇之deepClone

本文讨论数据的拷贝,并给出深拷贝的实现代码。

拷贝即复制( copy | clone ),获取指定数据副本的一种行为,理论上我们可以对任意类型的数据进行拷贝,包括但不限于null、undefined、字符串、数字、布尔值、对象、函数、数组、正则表达式等

数据的拷贝,可以具体的分为浅拷贝深拷贝。浅拷贝拷贝一层,副本可能存在共享问题,而深拷贝会拷贝多层,拷贝得到的副本无共享问题。

数据拷贝的方案有很多,譬如可以使用循环遍历和Object.assign()等方法,但这些拷贝方式都是浅拷贝。深拷贝的常见实现方案一种是利用 JSON 内置对象来进行序列化和反序列化操作,请看下面的代码:

ini 复制代码
var o = { name: "MiTaoEr", info: { address: "天津", color: "red" } };
var t = JSON.parse(JSON.stringify(o));
o.info.address = "北京";
console.log(t);
/* { name: 'MiTaoEr', info: { address: '天津', color: 'red' } } */

我们先通过JSON.stringify()方法将对象序列化为 JSON 字符串,然后再进行反序列化的骚操作再转换回来,顺利完成任务。不得不说,这种拷贝对象的方式,手起刀落干净利落,但却有一点点小遗憾,遗憾的是JSON 数据中没有函数和 undefined 类型,因此在进行序列化的过程中,对象中的这部分数据会被直接过滤掉,此外正则类型的数据也会被处理为空对象。

javascript 复制代码
var o = {
  name: "MiTaoEr",
  ID: undefined,
  showName: function() {},
  reg: /wen/,
  info: { address: "天津",color: "red" } };

console.log(JSON.parse(JSON.stringify(o)));
/* { name: 'MiTaoEr',reg: {},info: { address: '天津', color: 'red' } } */

利用 JSON 来实现深拷贝这种实现方式其实不够完美,下面给出通过递归来实现深拷贝的完美方案。

javascript 复制代码
        /* 深拷贝实现函数 */
        let deepClone = (val, wm = new WeakMap) => {
            if (val == null) return val;
            if (typeof val !== "object") return val;
            if (val instanceof Date) return new Date(val);
            if (val instanceof RegExp) return new RegExp(val);
            if (wm.has(val)) return wm.get(val);
            let _instance = new val.constructor;
            wm.set(val, _instance);

            for (let key in val) {
                if (val.hasOwnProperty(key)) _instance[key] = deepClone(val[key], wm);
            }
            return _instance;
        }

因为_instance是引用类型的数据,后续 for 循环的执行会更新_instance 的内容,考虑到循环引用的问题,在deepClone方法中用到WeakMap类型,其中wm.set方法执行后wm中保存的数据 key === value , 该对象用于数据的记忆。

ini 复制代码
        /* 001-测试代码:正常情况 */
        //var target = {name: "wen-ding-ding", car:{id: 666 }}

        /* 002-测试代码:循环引用 */
        var target = {name: "wen-ding-ding", car:{id: 666 }}
        target.ref = target;
        var result = deepClone(target);

        console.log(target, result);
        console.log(target.ref === result.ref, target.ref === target); /* false true */
        target.car.id = 888;
        console.log(result.car.id); /* 666 */

        /* 003-其它数据类型测试 */
        /* (1) 数组 */
        var arr1 = [100,200,300];
        var arr2 = deepClone(arr1);
        arr1.push(400);
        console.log(arr1,arr2);
        /* [ 100, 200, 300, 400 ] [ 100, 200, 300 ] */

        /* (2) 日期 */
        var date1 = new Date();
        var date2 = deepClone(date1);
        console.log(date1,date2,date1 == date2)
        /* 2019-08-24T11:00:21.379Z 2019-08-24T11:00:21.379Z false*/

        /* (3) null 和 undefined */
        console.log(deepClone(null),deepClone(undefined))
        /* null undefined */

        /* (4) 正则 */
        var reg1 = new RegExp(/\{\{(.+?)\}\}/);
        var reg2 = deepClone(reg1);
        console.log(reg1,reg2,reg1 == reg2)
        /* /\{\{(.+?)\}\}/ /\{\{(.+?)\}\}/ false */

        /* (5) 基本值 */
        console.log(deepClone("wen-ding-ding"),deepClone(2019),deepClone(true))
        /* wen-ding-ding 2019 true */
相关推荐
子兮曰20 分钟前
Bun v1.3.14 深度解析:Image API、HTTP/3、全局虚拟存储与五十项变革
前端·后端·bun
kyriewen1 小时前
今天,百年巨头一次砍了9200人,而一个离职科学家的实话让全网睡不着觉
前端·openai·ai编程
问心无愧05132 小时前
ctf show web 入门42
android·前端·android studio
kyriewen2 小时前
老板逼我上AI,我偷偷在浏览器里跑LLaMA,省下20万API费
前端·react.js·llm
Beginner x_u2 小时前
前端八股整理(手写 02)|数组转树、数组扁平化、随机打乱一个数组
前端·数组·数组转树·数组扁平化
KaMeidebaby2 小时前
卡梅德生物技术快报|禽类成纤维细胞 FISH 实验:鸟类性别染色体基因定位技术实现与数据验证
前端·数据库·其他·百度·新浪微博
天若有情6733 小时前
前端高阶性能优化:跳出传统懒加载与预加载,基于用户行为做轻量预判加载
前端·性能优化
小小小小宇3 小时前
前端转后端:SQL 是什么
前端
张元清4 小时前
React Observer Hooks:7 种监听 DOM 而不写样板代码的方式
前端·javascript·面试
广州华水科技4 小时前
单北斗GNSS变形监测是什么?主要有怎样的应用与优势?
前端