一文带你了解JavaScript基础之深拷贝和浅拷贝

目录
  • 浅拷贝
  • 深拷贝
  • 补充
  • 总结

拷贝(又名克隆,复制等),但是又分深拷贝和钱拷贝。

其实这个问题有时候想通了就很简单,如果想不通可能会有点绕,不过其难度比闭包等好理解的多。

为什么又这个概念的存在呢?先举一个例子。

var person={
    name:"张三",
    age:22
}
var person1=person;
console.log(person);
console.log(person1);

似乎可以被拷贝下来了,但是如果你操作person1的属性值,这个时候person属性值也会改变。

person1.name="李四";
console.log(person);
console.log(person1);

其实这个很容易理解,那就是上面只是两个对象的指针地址都是指向栈内存的相同位置,前面讲解引用数据类型的时候讲解过何为引用数据类型。

补充 :

对象.属性 和对象[属性]其实都是操作对象的属性值,只是两个不同的写法而已。

那说明这种指针赋值的方式不是拷贝,那什么就是拷贝呢,那就是一个新的对象用用了一个对象的所有属性,但彼此不受影响。

这样的理解就明白了,拷贝的本质就是将一个对象的属性循环赋值给一个新的对象而已。

那有为什么分浅拷贝和深拷贝,说实话浅拷贝和深拷贝本质上有什么什么区别吗?

其实本质没有区别,最大的区别就是考虑的条件以及拷贝过程中的属性类型不同而已。

老规矩先看代码

浅拷贝

var person={
    name:"张三",
    age:22
}
var person1={};
for( key in person){
    console.log(key);
    person1[key]=person[key];
}
console.log(person);
console.log(person1);

person1.name="李四";
console.log(person);
console.log(person1);

可以看出没有彼此影响,但是又会涉及到新的问题,那就是person对象的属性都是基本数据类型,如果是引用类型呢?比如数组,对象呢?

var person={
    name:"张三",
    age:22,
    son:{
        firstSon:"张大毛"
    }
}
var person1={};
for( key in person){
    console.log(key);
    person1[key]=person[key];
}
console.log(person);
console.log(person1);

现在修改一下person1的送属性。

person1.son={firstSon:"李大毛"};
console.log(person);
console.log(person1);

这样看似乎也没有彼此影响吗?但是这个前面说过对象.属性=这样等于重写赋值了person1.son的属性,自然会断开引用彼此的影响,毕竟两个地址不一样。但是如下修改呢?

person1.son.secondeSon="李大毛";
console.log(person);
console.log(person1);

惊不惊喜,意不意外还是彼此影响了,这个时候就需要一种新的操作了那就是深拷贝,说白了就是将属性值的有可能会为用引用类型。

补充:

如果person的原型上有属性值,也会被person1取到,赋值给person1.这个前面说到过,那样的话就会用到hasOwnProperty这个来判断是否属于自己的属性值。

深拷贝

其实深拷贝和浅拷贝的区别,相信看到这里几乎已经明白了差不多了,就是考虑属性值的类型而已。

// 上面补充了说了原型上的值也会被拷贝下来,为了方便直接用对象的原型链最后Object添加属性。
Object.prototype.address="你猜";
var person={
    name:"张三",
    age:22,
    son:{
        firstSon:"张大毛"
    }
}
strtype=Object.prototype.toString;
var person1={};
// 为了方便这个地方用递归方法
function coleFun(origin,target){
 // 防止目标对象本身有属性
    target=target||{}
    for( key in origin){
        if(origin.hasOwnProperty(key))
         if(strtype.call(origin[key])=="[object Object]"){
            target[key]={};
            target[key]=coleFun(origin[key],target[key])
         }else{
            target[key]=origin[key];
          }
    }
    return target;
}
person1=coleFun(person,person1)
console.log(person);
console.log(person1);

看结果没有问题 现象修改一下属性值试试

person1.son.secondeSon="李大毛";
console.log(person);
console.log(person1);

现在看没有问题。

所谓的深浅拷贝,说白了就是考虑属性值是否会有引用类型,然后在进行拷贝而已。如果上面代码没有看懂的话,可能需要重温一下引用数据和基本数据的区别,以及this指向,还有数据类型的判断方法。这写前面的文章都有聊过,可以翻看一下。

补充

有个朋友在评论区说如果如果用JavaScript中的JSON的方法进行拷贝数据,是深拷贝还是浅拷贝?

其实这个很容易证明的,那就是直接拷贝一个带有引用数据类型的对象,然后判断其是否会彼此影响即可。

首先看一下其含有的两种方法以及作用:

方法 作用
JSON.parse() 用于将一个JSON字符串转换为 JavaScript 对象。
JSON.stringify() 用于将 JavaScript 值转换为JSON字符串。

然后代码演示:

var person={
    name:"张三",
    age:22,
    son:{
        firstSon:"张大毛"
    }
}
var str=JSON.stringify(person);
var person1=JSON.parse(str);
console.log(person);
console.log(person1);

拷贝的目前看结果至少没有问题。

现在开始测试,是否相互影响

person1.son.secondeSon="李大毛";
console.log(person);
console.log(person1);

可见没有相互影响,其是通过JOSN进行拷贝其实是JavaScript一种常见的方式,毕竟比自己写要方便的很多。其本质就是将对象转成一个JSON格式的字符串,而通过字符串再生成一个对象而已,所以说其也是一种深拷贝。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 详解JS变量存储深拷贝和浅拷贝

    变量类型与存储空间 栈内存和堆内存 基本数据类型 string.number.null.undefined.boolean.symbol(ES6新增) 变量值存放在栈内存中,可直接访问和修改变量的值 基本数据类型不存在拷贝,好比如说你无法修改数值1的值 引用类型 Object Function RegExp Math Date 值为对象,存放在堆内存中 在栈内存中变量保存的是一个指针,指向对应在堆内存中的地址. 当访问引用类型的时候,要先从栈中取出该对象的地址指针,然后再从堆内存中取得所需的数据

  • js中的赋值 浅拷贝和深拷贝详细

    目录 1.js内存 2.赋值 3.浅拷贝 4.深拷贝 前言: 在学习下面文章前我们简单了解一下的内存的知识,以下先简要提一下 1.js内存 js内存,或者说大部分语言的内存都分为栈和堆.基本数据类型的变量值分配在栈上,引用数据类型的变量值分配在堆上,栈中只是存储具体堆中对象的地址. 2.赋值 对于基本数据类型,赋值操作是拷贝,即新旧变量不会相互影响. var a = 1; var b = a; b = 2; console.log(b); // 2 对于引用数据类型,赋值操作只是在栈中新增一个指

  • 浅谈JavaScript浅拷贝和深拷贝

    目录 一.直接赋值 二.浅拷贝 三.深拷贝 1. JSON对象的方式 2. 递归复制 网上关于这个话题,讨论有很多了,根据各路情况我自己整理了一下,最后还是能接近完美的实现深拷贝,欢迎大家讨论. javascript中的对象是引用类型,在复制对象的时候就要考虑是用浅拷贝还是用深拷贝. 一.直接赋值 对象是引用类型,如果直接赋值给另外一个对象,那么只是赋值一个引用,实际上两个变量指向的同一个数据对象,如果其中一个对象的属性变更,那么另外一个也会变更. 示例1,简单示例: let human1 =

  • javascript 关于赋值、浅拷贝、深拷贝的个人理解

    一.栈.堆.指针地址 栈内存:个人理解是,基本数据类型和引用数据类型都会用到的一个空间,这个空间以key-value形式存在,value本身不可修改,只能赋值替换: 堆内存:堆,就是堆积,每一个被开辟的空间可以想象成一个空纸盒子,纸盒子所在的纸盒子堆就是 "堆" .基本数据类型没有堆的概念.堆,只针对引用数据类型.存储方式应该是以对象(object)形式保存,对象内容包含key-value形式数据,value本身同样不可修改,只能赋值替换: 指针地址:针对引用数据类型在栈保存的值就是指

  • 详解JS深拷贝与浅拷贝

    一.预备知识 1.1.JS数据类型 基本数据类型:Boolean.String.Number.null.undefined 引用数据类型:Object.Array.Function.RegExp.Date等 1.2.数据类型的复制 基本数据类型的复制,是按值传递的 var a = 1; var b = a; b = 2; console.log(a); // 1 console.lob(b); // 2 引用数据类型的复制,是按引用传值 var obj1 = { a: 1; b: 2; }; v

  • JS对象复制(深拷贝和浅拷贝)

    一.浅拷贝 1.Object.assign(target,source,source...) a.可支持多个对象复制 b.如果source和target属性相同 source会复制target的属性 c.target只能为Object对象 var obj = {a:1,b:2} undefined Object.assign({c:3},obj) {c: 3, a: 1, b: 2} obj {a: 1, b: 2} 兼容性写法if(Object.assign){//兼容}else{//不兼容}

  • javascript二维数组和对象的深拷贝与浅拷贝实例分析

    本文实例讲述了javascript二维数组和对象的深拷贝与浅拷贝.分享给大家供大家参考,具体如下: 这篇文章主要为大家详细介绍了js实现数组和对象的深浅拷贝, 1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用 2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的"值"(数组的所有元素)拷贝过来,是"值"而不是"引用" JavaScript的数据类型分为原始数据类型和对象类型.二者在内存中存放的方式

  • JS赋值、浅拷贝和深拷贝(数组和对象的深浅拷贝)实例详解

    本文实例讲述了JS赋值.浅拷贝和深拷贝(数组和对象的深浅拷贝).分享给大家供大家参考,具体如下: 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的.  浅拷贝 只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做浅拷贝(浅复制) 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存.但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象. 赋值和浅拷贝的区别 当我们把一个对象赋值给一个新的变

  • 一文带你了解JavaScript基础之深拷贝和浅拷贝

    目录 浅拷贝 深拷贝 补充 总结 拷贝(又名克隆,复制等),但是又分深拷贝和钱拷贝. 其实这个问题有时候想通了就很简单,如果想不通可能会有点绕,不过其难度比闭包等好理解的多. 为什么又这个概念的存在呢?先举一个例子. var person={ name:"张三", age:22 } var person1=person; console.log(person); console.log(person1); 似乎可以被拷贝下来了,但是如果你操作person1的属性值,这个时候person

  • JavaScript基础心法 深浅拷贝(浅拷贝和深拷贝)

    前言 说到深浅拷贝,必须先提到的是JavaScript的数据类型,之前的一篇文章JavaScript基础心法--数据类型说的很清楚了,这里就不多说了. 需要知道的就是一点:JavaScript的数据类型分为基本数据类型和引用数据类型. 对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的. 浅拷贝 浅拷贝的意思就是只复制引用,而未复制真正的值. const originArray = [1,2,3,4,5]; const originObj = {a:'a'

  • 一文带你了解JavaScript垃圾回收机制

    目录 1. 概述 2. 内存管理 3. 垃圾回收 4. GC算法介绍 5. 引用计数算法 1. 引用计数优缺点 6. 标记清除算法 1. 标记清除算法优缺点 7. 标记整理算法 8. 执行时机 9. V8引擎 1. 垃圾回收策略 2. 回收新生代对象 3. 回收老生代对象 4. V8垃圾回收总结 10. Performance工具介绍 1. 内存问题的体现 2. 监控内存的几种方式 3. 任务管理器监控内存 4. TimeLine记录内容 5. 堆快照查找分离DOM 6. 判断是否存在频繁GC

  • 一文带你了解Python 四种常见基础爬虫方法介绍

    一.Urllib方法 Urllib是python内置的HTTP请求库 import urllib.request #1.定位抓取的url url='http://www.baidu.com/' #2.向目标url发送请求 response=urllib.request.urlopen(url) #3.读取数据 data=response.read() # print(data) #打印出来的数据有ASCII码 print(data.decode('utf-8')) #decode将相应编码格式的

  • JavaScript一文带你玩转web表单网页

    一.前言 前面我们介绍了web网页的快速开发,这次我们讲点更深层次些的,看这面之前建议先看 上篇,之后在食用这篇. 二.正文部分 如图示:点击webapp上面的小三角形点到直到看到jsp位置 我们在创建好了之后这里会有jsp的空单子,我们在这输入的内容,会先反馈到前端,之后再进行 后端数据处理和接收. 第一步:我们先在这输入一些东西如图:其中<h1>内容</h1>这是格式,说明中间的内容是 一个h1 大小的标题,h1--h6标题在逐渐减小,要慎用h1,因为h1比较大 要先点击这个运

  • 一文带你玩转JavaScript的箭头函数

    目录 箭头函数 语法规则 简写规则 常见应用 map filter reduce 箭头函数中的this使用 concat this的查找规则 箭头函数 在ES6中新增了函数的简写方式----箭头函数,箭头函数的出现不仅简化了大量代码,也让代码看起来更加优雅,同时也解决了this指向问题,下面我们就来详细讲解如何玩转箭头函数. 语法规则 1.之前的方法 function foo1(){} var foo2 = function(name,age){ console.log("函数体代码"

  • 一文带你搞懂JavaScript中的进制与进制转换

    目录 进制介绍 进制转换 parseInt(str, radix) Number() +(一元运算符) Number.prototype.toString(radix) 自定义转换 十进制与十六进制转换 十进制和二进制转换 进制介绍 JavaScript 中提供的进制表示方法有四种:十进制.二进制.十六进制.八进制. 对于数值字面量,主要使用不同的前缀来区分: 十进制(Decimal):取值数字 0-9:不用前缀. 二进制(Binary):取值数字 0 和 1 :前缀 0b 或 0B. 十六进制

  • Javascript基础学习笔记(菜鸟必看篇)

    什么是变量? 变量是用于存储信息的容器 变量的声明 语法: var 变量名 变量名 = 值; 变量要先声明再赋值 变量可以重复赋值 变量的命名规则 变量必须以字母开头: 变量也能以$和_符号开头(不过我们不推荐这么做): 变量名称对大小写敏感(a和A是不同的变量). 语句 语句以一个分号结尾:如果省略分号,则由解析器确定语句的结尾. 有个好的编码习惯,都要以 ; 结尾 数据类型 在JavaScript中,一段信息就是一个值(value).值有不同的类型,大家最熟悉的类型是数字.字符串(strin

  • 一文带你走进js-数据类型与数据结构的世界

    目录 2. 数据类型 2.1 原始类型(6 种原始类型,使用 typeof 运算符检查) 2.2 null 与 Object 3. 原始值 3.1 原始值基本概念 3.2 各类型说明 1. 什么叫动态类型 JavaScript 是一种弱类型或者说动态语言. 我们不用提前声明变量的类型,在程序运行过程中,类型会被自动确定. 这也意味着你可以使用同一个变量保存不同类型的数据 let a; // 初始不给任何类型 a = 11; // a此时是number类型 a = "二大爷"; // a

  • 一文带你走进js数据类型与数据结构的世界

    目录 1. 什么叫动态类型 2. 数据类型 2.1 原始类型(6 种原始类型,使用 typeof 运算符检查) 2.2 null 与 Object 2.3 typeof 操作符重点说明 3. 原始值 3.1 原始值基本概念 3.2 各类型说明 1. 什么叫动态类型 JavaScript 是一种弱类型或者说动态语言. 我们不用提前声明变量的类型,在程序运行过程中,类型会被自动确定. 这也意味着你可以使用同一个变量保存不同类型的数据 let a; // 初始不给任何类型 a = 11; // a此时

随机推荐