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

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

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

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

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 类型,因此在进行序列化的过程中,对象中的这部分数据会被直接过滤掉,此外正则类型的数据也会被处理为空对象。

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 来实现深拷贝这种实现方式其实不够完美,下面给出通过递归来实现深拷贝的完美方案。

        /* 深拷贝实现函数 */
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 , 该对象用于数据的记忆。

        /* 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 */

前端开发系列120-进阶篇之deepClone的更多相关文章

  1. openlayers5-webpack 入门开发系列一初探篇(附源码下载)

    前言 openlayers5-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载 ...

  2. leaflet-webpack 入门开发系列一初探篇(附源码下载)

    前言 leaflet-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载地址 w ...

  3. 【Windows10 IoT开发系列】配置篇

    原文:[Windows10 IoT开发系列]配置篇 Windows10 For IoT是Windows 10家族的一个新星,其针对不同平台拥有不同的版本.而其最重要的一个版本是运行在Raspberry ...

  4. ESP8266开发之旅 进阶篇② 闲聊Arduino IDE For ESP8266烧录配置

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  5. 【webpack 系列】进阶篇

    本文将继续引入更多的 webpack 配置,建议先阅读[webpack 系列]基础篇的内容.如果发现文中有任何错误,请在评论区指正.本文所有代码都可在 github 找到. 打包多页应用 之前我们配置 ...

  6. iOS开发系列--Swift进阶

    概述 上一篇文章<iOS开发系列--Swift语言>中对Swift的语法特点以及它和C.ObjC等其他语言的用法区别进行了介绍.当然,这只是Swift的入门基础,但是仅仅了解这些对于使用S ...

  7. 旨在脱离后端环境的前端开发套件 - IDT Server篇

    IDT,一个基于Nodejs的,旨在脱离后端环境的前端开发套件,目的就是能让前端开发完全脱离后端的环境,无论后端是什么模板引擎(主流),都能应付自如. IDT主要包括两大部分:Server + Bui ...

  8. 前端开发【第2篇:CSS】

    鸡血 样式的属性多达几千个,但别担心,按照80-20原则,常用的也就几十个,你完全可以掌握它. Css初识 HTML的诞生 早期只有HTML的时候为了让HTML更美观一点,当时页面的开发者会把颜色写到 ...

  9. [置顶]【实用 .NET Core开发系列】- 导航篇

    前言 此系列从出发点来看,是 上个系列的续篇, 上个系列因为后面工作的原因,后面几篇没有写完,后来.NET Core出来之后,注意力就转移到了.NET Core上,所以再也就没有继续下去,此是原因之一 ...

  10. openlayers4 入门开发系列之风场图篇

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

随机推荐

  1. astc内存大小计算方式

    https://www.cnblogs.com/bylle/p/12212823.html

  2. Go单元测试与报告

    1.编写代码 1)打卡GoLand,新建项目命名为gotest 2)在gotest目录下新建两个go file,如下图所示: 其中CircleArea.go为计算圆面积的待测go程序,代码如下: pa ...

  3. python,批量修改文件后缀名

    比如,D盘test目录下有以下几个没有后缀的文件,需要修改为txt结尾 python代码如下 # python批量更换后缀名 import os import sys #需要修改后缀的文件目录 os. ...

  4. Joomla未授权访问漏洞|CVE-2023-23752复现及修复

      00 前言 这漏洞公开有阵子了好像,今天才复现了下 Jooml 在海外使用较多,是一套使用 PHP 和 MySQL 开发的开源.跨平台的内容管理系统(CMS).Joomla 4.0.0 至 4.2 ...

  5. KoalaWiki vs DeepWiki:更优秀的开源代码知识库解决方案

    KoalaWiki vs DeepWiki:更优秀的开源代码知识库解决方案 资源链接: 教程代码仓库:https://github.com/AIDotNet/SemanticKernel.Sample ...

  6. Java编程--设计模式之装饰者模式

    目录 装饰者模式 简介 做馒头实例 生产汽车实例 常见使用 装饰者模式 简介 装饰者模式的主要功能就是对一个类的功能进行扩充! 对于需要对某个类扩充,但是该类是final类,不能被继承,这是时候可以用 ...

  7. rancher 卸载后重装报错

    报错信息 kubectl create namespace cattle-system Error from server (InternalError): Internal error occurr ...

  8. Pytorch之线性回归

    从零开始实现 %matplotlib inline import torch import numpy as np import random 生成数据集 设训练数据集样本数为1000,特征数为2,使 ...

  9. 赛前十天——递归(easy)

    *理论上,递归与循环是等价的,即任何循环都可以重写为递归形式 eg: package javaPractice; public class Contest {         public stati ...

  10. Java 提取url的域名

      有时候,我们需要校验URL的域名是否在白名单中,故需要提取其中的域名.可以使用java标准类库java.net.URL进行提取,方法如下: import org.apache.commons.la ...