IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JS中的浅拷贝和深拷贝 -> 正文阅读

[JavaScript知识库]JS中的浅拷贝和深拷贝

前言

在js中涉及到深浅拷贝的时候,就会牵扯到

栈和堆

在编译期间,除了声明变量和函数,查找函数中的标识符这两项工作之外,还有内存分配,不同类型的数据会分配到不同的内存空间。

栈内存
举个栗子,就比如有一个书架,每本书对应一个书格,只有先拿出来才能再放进去,是一个先出后进的顺序。这个书架就可以比作栈内存。

  • 栈内存中主要保存基本数据类型值和引用数据类型值的地址。
  • 栈内存是存在大小限制和储存上限的,就是一简单存储。
  • 栈内存是自动分配内存大小的,读取数据快,写入速度快,但是储存内容少,变量一旦不使用系统会自动清除掉释放内存。

堆内存
堆内存就好比一个百宝箱,这个时候我们不需要先出后进的操作,只需要知道我们想要什么东西,根据这个东西的名称直接去拿就行了。

  • 主要保存一组无序且唯一的引用数据类型值,可以使用栈内存中的键名来获取。
  • 堆内存的存储数据多,读写速度慢,一般对象会储存在堆中,对内存的大小和限制都是未知的。
  • 堆内存是动态分配内存的,也不一定会自动释放内存。
    在这里插入图片描述

赋值和赋址

先来简单说一下
赋值指的就是把堆内存中的对象数据获取到存放到栈内存中,就相当于在栈内存中新创建了一个值然后把该值赋给新创建的变量;

赋址指的就是把堆内存的数据值存放到栈内存中,并且地址也是指向堆内存的,就相当于在栈内存中创建一个指针,这个指针指向堆内存中的值。

看段代码:

function a(){
    var a=[1,2,3,4,5];	//在堆内存中存储一个arry
    var s=a;			//赋址操作,在栈中创建一个叫s的指针指向堆内存中的a值
    var d=a[2];			//赋值操作,在栈中创建一个新值,并把堆中a数组的某个值赋值给栈中的新值
    console.log(s);		//[1,2,3,4,5]
    console.log(d);		//3

    s[0]=6;				//改变的是堆中a的值,因为s是指向a的
    d=7;				//无法改变a中对应的值,因为d是在栈中新建的一个变量,这是它已经和堆没有关联了
    console.log(s);		//[6,2,3,4,5]
    console.log(d);		//7
    console.log(a);		//[6,2,3,4,5]
}

从上面的代码我们可以知道,改变了s的数据,同时会改变堆中a的对应值;改变d的值,堆中a的对应值是不会做出改变的。

这就是赋值和赋址的区别。

浅拷贝和深拷贝

所谓拷贝,就是赋值的意思;把一个值赋值给另一个值。

基本数据类型赋值的时候,赋值的是数据,所以并不存在浅拷贝和深拷贝的问题。

浅拷贝

创建一个新的对象值,给这个对象赋原始对象的属性值,指向不同的地址。也就是说,浅拷贝是拷贝的原始对象的属性值,如果拷贝的这个值是引用类型,拷贝的就是原始对象的地址,拷贝的不深所以叫浅拷贝。

  • 拷贝原对象中的基本数据类型值,是不会改变原对象的。
  • 如果拷贝的是原对象中子对象的引用数据类型值,是会改变原对象的值;因为在堆中,对象里面的子对象会和主对象共享地址,这个时候原对象也会跟着改变。

栗子:

1,使用for…in…实现浅拷贝

// 使用for...in..实现浅拷贝
function s(){
    // 原对象,【堆中】
    var _data={
        id:1,
        num:10,
        list:{
            id:1,
            num:1
        }
    };

    var _newData={};    //新创建一个对象,【栈中】

    // 使用for...in...进行拷贝操作
    for(var i in _data){
        _newData[i]=_data[i];
    }

    // 对浅拷贝的新对象做操作
    _newData.id=2;
    _newData.list.id=2;

    console.log('_data值:',_data)
    console.log('_newData值:',_newData)

    // 输出
    // _data值:,{"id":1,"num":10,"list":{"id":2,"num":1}}
    // _newData值:,{"id":2,"num":10,"list":{"id":2,"num":1}}
}

从面的栗子可以很清楚的看到,浅拷贝确实是满足我们上面说的两个小点。

2,使用Object.assign()实现浅拷贝

// 使用Object.assign()实现浅拷贝
function d(){
    // 原对象,【堆中】
    var _data={
        id:1,
        num:10,
        list:{
            id:1,
            num:1
        }
    };

    var _newData={};    //新创建一个对象,【栈中】

    // 使用Object.assign(新对象,需要拷贝的对象)进行拷贝操作
    Object.assign(_newData,_data)

    // 对浅拷贝的新对象做操作
    _newData.id=2;
    _newData.list.id=2;

    console.log('_data值:',_data)
    console.log('_newData值:',_newData)

    // 输出
    // _data值:,{"id":1,"num":10,"list":{"id":2,"num":1}}
    // _newData值:,{"id":2,"num":10,"list":{"id":2,"num":1}}
}

这个方法相对于for…in…简单很多。

深拷贝

深拷贝不仅仅只是把原对象赋值给新对象,并且还会把原对象中子对象都原模原样赋值给新对象,这样修改新对象后就不会改变原对象的值。

栗子:

1,使用JSON.stringify()实现深拷贝

需要注意的是这种方法无法拷贝,正则表达式,undefined,function

// 使用JSON.stringify()进行深拷贝
function f(){
    // 原对象,【堆中】
    var _data={
        id:1,
        num:10,
        list:{
            id:1,
            num:1
        }
    };

    var _newDataStr=JSON.stringify(_data),
        _newData=JSON.parse(_newDataStr);

     // 对浅拷贝的新对象做操作
    _newData.id=2;
    _newData.list.id=2;

    console.log('_data值:',_data)
    console.log('_newData值:',_newData)  

    // 输出
    // _data值:,{"id":1,"num":10,"list":{"id":1,"num":1}}
    // _newData值:,{"id":2,"num":10,"list":{"id":2,"num":1}} 
}

2,使用递归的方式实现深拷贝

// 使用递归的方式进行深拷贝
function g(data) {
    // 首先判断data是基本数据类型还是引用数据类型,如果是基本直接返回
    if (typeof data != "object") {
        return data
    }

    // 根据是数组还是对象进行新建变量
    var _newData = Array.isArray(data) ? [] : {};

    // 判断是数组还是对象
    if (data instanceof Array) {
        console.log("是数组")
        // 数组
        for (var i = 0; i < data.length; i++) {
            _newData[i] = data[i]
            if (typeof _newData[i] == 'object') {
                g(_newData[i]);
            }
        }
    } else {
        console.log("是对象")
        // 对象
        for (var i in data) {
            // 判断对象里面是否有数组或者对象
            if (typeof data[i] == 'object') {
                _newData[i] = g(data[i])
            } else {
                _newData[i] = data[i]
            }
        }
    }
    return _newData
}
console.log(g({ a: 1, s: 2, d: [1, 2, 3] }));		//{"a":1,"s":2,"d":[1,2,3]}
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-30 12:28:21  更:2021-10-30 12:30:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 22:11:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码