实现深拷贝的多种方式

Souleigh ✨ 等级 649 0 0
标签: 拷贝前端

实现深拷贝的多种方式

简单来说,深拷贝主要是将另一个对象的属性值拷贝过来之后,另一个对象的属性值并不受到影响,因为此时它自己在堆中开辟了自己的内存区域,不受外界干扰。 浅拷贝主要拷贝的是对象的引用值,当改变对象的值,另一个对象的值也会发生变化。 1.简单深拷贝(一层浅拷贝)

①for循环拷贝

// 只复制第一层的浅拷贝

function simpleCopy(obj1) {
   var obj2 = Array.isArray(obj1) ? [] : {};
   for (let i in obj1) {
   obj2[i] = obj1[i];
  }
   return obj2;
}
var obj1 = {
   a: 1,
   b: 2,
   c: {
   d: 3
  }
}
var obj2 = simpleCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4

②Object.assign()实现一层深拷贝

 var obj1 = {
    a: 1,
    b: 2,
    c: 3
}
var obj2 = Object.assign({}, obj1);
obj2.b = 5;
console.log(obj1.b); // 2
console.log(obj2.b); // 5
 var obj1 = {
    a: 1,
    b: 2,
    c: ['a','b','c']
}
var obj2 = Object.assign({}, obj1);
obj2.c[1] = 5;
console.log(obj1.c); // ["a", 5, "c"]
console.log(obj2.c); // ["a", 5, "c"]

③slice实现

// 对只有一级属性值的数组对象使用slice
var a = [1,2,3,4];
var b = a.slice();
b[0] = 2;
alert(a); // 1,2,3,4
alert(b); // 2,2,3,4
// 对有多层属性的数组对象使用slice
var a = [1,[1,2],3,4];
var b = a.slice();
b[1][0] = 2;
alert(a); // 1,2,2,3,4
alert(b); // 1,2,2,3,4

④通过Object.create()实现

function deepCopy(obj) {
  var copy = Object.create(Object.getPrototypeOf(obj));
  var propNames = Object.getOwnPropertyNames(obj);

  propNames.forEach(function(name) {
    var desc = Object.getOwnPropertyDescriptor(obj, name);
    Object.defineProperty(copy, name, desc);
  });

  return copy;
}

var obj1 = { a: 1, b: {bc: 50, dc: 100, be: {bea: 1}} };
var obj2 = deepCopy(obj1);
obj2.a = 20;
obj2.b.bc = 60;
console.log(obj1.a)//1
console.log(obj2.a)//20
console.log(obj1.b.bc)//60
console.log(obj2.b.bc)//60

2.粗暴深拷贝(抛弃对象的constructor)

使用jsON.stringify和jsON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;

function deepCopy(obj1){
    let _obj = JSON.stringify(obj1);
    let obj2 = JSON.parse(_obj);
    return obj2;
}
var a = [1, [1, 2], 3, 4];
var b = deepCopy(a);
b[1][0] = 2;
alert(a); // 1,1,2,3,4
alert(b); // 2,2,2,3,4

缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;

let obj1 = {
   fun:function(){
      alert(123);
   }
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun); // function
console.log(typeof obj2.fun); // undefined

3.复杂深拷贝(相对完美)

递归拷贝实现深拷贝,解决循环引用问题

/**
 * 判断是否是基本数据类型
 * @param value
 */
function isPrimitive(value){
  return (typeof value === 'string' ||
  typeof value === 'number' ||
  typeof value === 'symbol' ||
  typeof value === 'boolean')
}

/**
 * 判断是否是一个js对象
 * @param value
 */
function isObject(value){
  return Object.prototype.toString.call(value) === "[object Object]"
}

/**
 * 深拷贝一个值
 * @param value
 */
function cloneDeep(value){

  // 记录被拷贝的值,避免循环引用的出现
  let memo = {};

  function baseClone(value){
    let res;
    // 如果是基本数据类型,则直接返回
    if(isPrimitive(value)){
      return value;
    // 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值
    }else if(Array.isArray(value)){
      res = [...value];
    }else if(isObject(value)){
      res = {...value};
    }

    // 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝
    Reflect.ownKeys(res).forEach(key=>{
      if(typeof res[key] === "object" && res[key]!== null){
        //此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题
        if(memo[res[key]]){
          res[key] = memo[res[key]];
        }else{
          memo[res[key]] = res[key];
          res[key] = baseClone(res[key])
        }
      }
    })
    return res;
  }

  return baseClone(value)
}

4.ES插件lodash

import lodash from 'lodash'

var objects = [1,{ 'a': 1 }, { 'b': 2 }];
var deep = lodash.cloneDeep(objects);
deep[0] = 2;
deep[1].a = 2;
console.log(objects[0]);//1
console.log(deep[0]);//2
console.log(objects[1].a);//1
console.log(objects[1].a);//2

收藏
评论区

相关推荐

实现深拷贝的多种方式
实现深拷贝的多种方式 简单来说,深拷贝主要是将另一个对象的属性值拷贝过来之后,另一个对象的属性值并不受到影响,因为此时它自己在堆中开辟了自己的内存区域,不受外界干扰。 浅拷贝主要拷贝的是对象的引用值,当改变对象的值,另一个对象的值也会发生变化。 1.简单深拷贝(一层浅拷贝) ①for循环拷贝 // 只复制第一层的浅拷贝 javascript func
Vue - 深入浅出 Mixin
什么 mixinmixin, 意为混入。比如去买冰激凌,我先要一点奶油的,再来点香草的。我就可以吃一个奶油香草的冰激凌。如果再加点草莓,我可以同时吃三个口味的冰激凌。 代码表示假设把你已有的奶油味的称为 base,把要添加的味道称为 mixins。用 js 伪代码可以这么来写:const base     hasCreamFlavor()      re
打算或已经从事计算机行业的你,请一定不要错过
理解计算机系统的必选书目计算机从业人员必读宝典 下面请欣赏《深入理解计算机系统》的那些“名场面”吧↓ ▲这是一本已经“翻烂”的《深入理解计算机系统》 ▲睡不着的夜晚,来点《深入理解计算机系统》 ▲完全不懂怎么“深入理解计算机系统”▲拥挤的地铁车厢,那一抹深邃的蓝 ▲难啃程度与学习日语不相上下 ▲考研标配6件套 ▲据说包里背着《深入理解
JAVA回调机制(CallBack)之小红是怎样买到房子的??
JAVA回调机制CallBack 序言最近学习java,接触到了回调机制(CallBack)。初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义。当然了,我在理解了回调之后,再去看网上的各种讲解,确实没什么问题。但是,对于初学的我来说,缺了一个循序渐进的过程。此处,将我对回调机制的个人理解,按
深圳社招大厂面试分享
先了解清楚,看准了再下手。 3月1号高铁到达深圳,到今天第九天了,四家大公司二家中小型公司,有几家已经面了几轮,下周还要面,挂了几家,不过目前已经选择了一家。总结一下找工作这段时间的经历,问得最多的是自定义 View 基本每家都问,问 View 的绘制流程,自定义View的步骤,有时会涉及到细节比如 PhoneWindow 实例是在哪个类哪个方法中实例化的,
Java深究之Vector、ArrayList、LinkedList的区别
        在java开发中除了上文经常用的对字符串的操作外,还有使用居多的当属集合了。在基础的java学习时,相信很多同学都学习了List、Set、Map这些,他们之间的区别和基本的使用方法也是很了解了,本文是详细的去分析List中Vector、ArrayList、LinkedList之间的区别和底层实现原理以及使用场景 首先说下这三者的区别: *
NIO零拷贝的深入分析
深入分析通过Socket进行数据文件传递中的传统IO的弊端以及NIO的零拷贝实现原理,及用户空间和内核空间的切换方式 <!--more--> ### 传统的IO流程 ![图片](https://oscimg.oschina.net/oscnet/e02779c4d19303c03ad070f9b0ad06a4588.png) 在这个过程中: 1.
PHP程序员的技术成长规划 第三阶段:高级阶段
**第三阶段:高级阶段 (高级PHP程序员)** **重点:**除了基本的LNMP程序,还能够在某个方向或领域有深入学习。(纵深维度发展) **目标:**除了能够完成基本的PHP业务开发,还能够解决大部分深入复杂的技术问题,并且可以独立设计完成中大型的系统设计和开发工作;自己能够独立hold深入某个技术方向,在这块比较专业。(比如在MySQL、Ngi
Ubuntu 下安装深度音乐播放器
网上搜了一个安装深度音乐的有问题。 我自己又整理一个最新的2014版的深度音乐。 Linux环境:Ubuntu14.04 LTS 64bit 深度音乐都是DEB包安装。 下面,介绍如何安装深度音乐播放器。 需要python-deepin-utils、deepin-ui、gstreamer0.10-ffmpeg、python-deepin-g
Android:Layout_weight的深刻理解
最近写Demo,突然发现了Layout\_weight这个属性,发现网上有很多关于这个属性的有意思的讨论,可是找了好多资料都没有找到一个能够说的清楚的,于是自己结合网上资料研究了一下,终于迎刃而解,写出来和大家分享。 首先看一下Layout\_weight属性的作用:它是用来分配属于空间的一个属性,你可以设置他的权重。很多人不知道剩余空间是个什么概念,下面
HDOJ1518Square 深搜
Square Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 11099 Accepted Submission(s): 3566 Problem Description Given a
JavaScript的深拷贝和浅拷贝
**一、数据类型** 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。、 1.基本数据类型的特点:直接存储在栈(stack)中的数据 2.引用数据类型的特点:**存储的是该对象在栈中引用,真实的数据放在堆内存里**。 引用数据类型在栈中存储了指针,该指针指向堆中该实
MindSpore手写数字识别初体验,深度学习也没那么神秘嘛
> **摘要**:想了解深度学习却又无从下手,不如从手写数字识别模型训练开始吧! 深度学习作为机器学习分支之一,应用日益广泛。语音识别、自动机器翻译、即时视觉翻译、刷脸支付、人脸考勤……不知不觉,深度学习已经渗入到我们生活中的每个角落,给生活带来极大便利。即便如此,依然有很多人觉得深度学习高深莫测、遥不可及,的确,它有深奥之处,非专业人士难以企及,但也有亲
Python3+TensorFlow 打造人脸识别智能小程序
第1章 课程导学 本章节主要介绍课程的主要内容、核心知识点、课程涉及到的应用案例、深度学习算法设计通用流程、适应人群、学习本门课程的前置条件、学习后达到的效果等,帮助大家从整体上了解本门课程的整体脉络。 第2章 深度学习基础串讲(必备理论知识) 主要介绍深度学习的基础知识,具体包括了深度学习的发展历程、基本概念(前向运算、反向传播、参数优化)、深度
ThreadLocal的深入理解及应用
**是什么**? **ThreadLocal**很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread , 它类似(Map),用来存储当前运行线程及对应的变量。 在WEB应用中每次Http请求,都相当于从线程池取一个空闲线程对请求的方法作处理。此时当前线程的所有方法中Thread.currentThread