zoukankan      html  css  js  c++  java
  • js对象拷贝

    js的内存结构

    原始类型与引用类型

    六大原始数据类型:String、Number、Boolean、Null、Undefined、Symbol。对这些简单数据类型(原始值)的访问是按值访问的,因此我们操作的就是存储在变量中的实际值。

    引用数据类型:object。保存引用值得变量时按引用访问的,不能直接操作对象所在内存空间,在操作对象时,实际上操作的时对该对象的引用,而非实际的对象本身。

    可以认为原始类型,是以键值对的形式存储在栈中。而引用类型的只是将地址存储在栈中,对象本身存储到堆内存中了,我们无法直接访问。

     

    • 对对象复制(浅拷贝)

    当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。

    var oldobj = {
                name: '小二上酒',
                age: 22,
                arr: [1, 2, 3, { key: '123' }],  //数组测试    
            };
            let newobj = oldobj
            newobj.name = '不喝酒';
            console.log(oldobj);
            console.log(newobj);

     可以看到,浅拷贝只不过是将oldobj地址复制了一份传给newobj,他们实际上指的还是同一个内存中的对象。操作newobj指向的对象时,即也在操作lodobj指向的对象,总而言之对象还是那个对象。

    • 深拷贝

    我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。深拷贝则是,完完全全将栈内存中存放的地址和堆内存中的对象都拷贝(创建)一份,生成一个新的对象。既然属性值类型是数组和或象时只会传地址址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可

     1 function deepCopy(target={}){
     2             if(typeof target !=='object' || target == null){
     3                 return target
     4             }
     5             let result
     6             if(target instanceof Array){
     7                 result =[]
     8             }else{
     9                 result={}
    10             }
    11             for(let key in target){
    12                 result[key] = deepCopy(target[key])
    13             }
    14             return result
    15         }
    16 
    17         var oldobj = {
    18             name: '小二上酒',
    19             age: 22,
    20             arr: [1, 2, 3, { key: '123' }],  //数组测试    
    21         };
    22         let newobj = deepCopy(oldobj)
    23         newobj.name = '不喝酒';
    24         console.log(oldobj);
    25         console.log(newobj);  //

     可以看到这样一来对复制后得到的newobj对象进行修改,就不会影响到oldobj了。

    但是这样对一些特殊的情况并不适用。我们还需要解决以下几个问题

    • 对象内属性循环引用自身

    • 相同的引用

    • 其余类型

    解决前俩点的完善写法

     1         
     2         function deepCopy(target) {
     3             let copyed_objs = [];//此数组解决了循环引用和相同引用的问题,它存放已经递归到的目标对象 
     4 
     5             function _deepCopy(target) {
     6                 if ((typeof target !== 'object') || !target) {
     7                     return target;
     8                 }
     9                 // 如果当前目标对象和 copyed_objs 中的某个对象相等,那么不对其递归。
    10                 for (let i = 0; i < copyed_objs.length; i++) {
    11                     if (copyed_objs[i].target === target) {
    12                         return copyed_objs[i].copyTarget;
    13                     }
    14                 }
    15                 //处理target是数组的情况 
    16                 let result;
    17                 if (target instanceof Array) {
    18                     result = [];
    19                 } else {
    20                     result = {}
    21                 }
    22                 copyed_objs.push({ target: target, copyTarget: result })
    23 
    24                 // 递归实现深层次拷贝
    25                 for(let key in target){
    26                     result[key] = _deepCopy(target[key])
    27                 }
    28                 return result;
    29             }
    30             return _deepCopy(target);
    31         }
    32 
    33 
    34         var oldobj = {
    35             name: '小二上酒',
    36             age: 22,
    37             arr: [1, 2, 3, { key: '123' }],  //数组测试    
    38         };
    39 
    40         oldobj.self = oldobj //循环引用测试
    41         oldobj.likes = { book: '剑来' }
    42         oldobj.hobby = oldobj.likes //相同引用测试
    43         let newobj = deepCopy(oldobj)
    44         console.log(oldobj);
    45         console.log(newobj);

    可以看出,尽管对oldobj添加循环引用自身的属性,以及相同引用的属性,依然可以完成深拷贝。

    ps:JSON.sringify 和 JSON.parse 

    1         var oldobj = {
    2             name: '小二上酒',
    3             age: 22,
    4             arr: [1, 2, 3, { key: '123' }],  //数组测试    
    5         };
    6         let newobj = JSON.parse(JSON.stringify(oldobj))
    7         console.log(oldobj);
    8         console.log(newobj);

    这是JS实现深拷贝最简单的方法了,原理就是先将对象转换为字符串,再通过JSON.parse重新建立一个对象。适合项目中快捷使用。 但是这种方法的局限也很多:

    • 不能复制function、正则、Symbol
    • 循环引用报错
    • 相同的引用会被重复复制

    学习于:https://juejin.cn/post/6844903620190666759

    https://www.cnblogs.com/web1/p/6518482.html

  • 相关阅读:
    Android 常见adb命令
    下载安装JDK,并且配置java环境变量
    安装黑苹果教程
    创建不死目录、不死文件
    win10下安装centos7双系统
    Hadoop 3.0完全分布式集群搭建方法(CentOS 7+Hadoop 3.2.0)
    Hadoop 2.0完全分布式集群搭建方法(CentOS7+Hadoop 2.7.7)
    启动HBase脚本start-hbase.sh时报Class path contains multiple SLF4J bindings.解决方法
    HQuorumPeer和QuorumPeerMain进程的区别
    Zookeeper集群安装与配置
  • 原文地址:https://www.cnblogs.com/zxf906/p/15328239.html
Copyright © 2011-2022 走看看