精华内容
下载资源
问答
  • 怎么拷贝
    千次阅读
    2022-03-28 11:02:03

    目录

    一、深浅拷贝是什么?

    二、深浅拷贝基础(数据类型)

    1.基础数据类型(值传递)

    2.复杂数据类型(地址传递)

    三、深浅拷贝怎样操作(代码示例)

     1.浅拷贝:

    1)通过Object.assign     for   in  进行浅拷贝 

     2.深拷贝:

    1)深拷贝最简单的实现是: JSON.parse(JSON.stringify(obj))

     2)实现一个 deepClone 函数  (深拷贝,完美)

    3)递归拷贝


    一、深浅拷贝是什么?

    首先我们要明白一点,js中数据类型分为:

            基本数据类型 (Number, String, Boolean, Null, Undefined, Symbol)
            对象数据类型 ( Object )
            引用数据类型的值是保存在栈内存和堆内存中的对象。栈区内存保存变量标识符和指向堆内存中该对象的指针。当寻找引用值时,解释器会先寻找栈中的地址。然后根据地址找到堆内存的实体。

    浅拷贝:

            浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
            可以使用 for in、 Object.assign、 扩展运算符 ... 、Array.prototype.slice()、Array.prototype.concat() 等

    深拷贝:

            深拷贝和浅拷贝是针对复杂数据类型(对象及数组)来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。

            深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。

    二、深浅拷贝基础(数据类型)

    1.基础数据类型(值传递)

    //基本数据类型的拷贝(复制copy)  深拷贝和浅拷贝
    
    //前提:需要理解  值传递   地址传递 基本数据类型    值传递    number  string  boolean null  undefined   Sysmbol  bigInt       
        // Number
             a = 1.1;b = a;b = 2; console.log(a,b)
        // String
             a = 'hello';b = a;b = 3; console.log(a,b)
        // Boolean
             a = false;b = a;b = 'sss'; console.log(a,b)
        // Undefined
             a = undefined;b = a;b = false; console.log(a,b)
        // Null
             a = null;b = a;b = undefined; console.log(a,b)

    2.复杂数据类型(地址传递)

    //复杂数据类型(object) 的拷贝     地址传递  注意 常用的复杂数据类型包括:{} 、[] 、function(){} 、Date 、RegExp 、null(这个比较特殊)等
      
       //1、我们依然用一的简单赋值(=)来进行一遍操作(赋值)
      // 经过实践我们会发现:
       //  1、当类型为{}、[]的时候,改变b的值,a也会跟着一起变化。
        // 2、当类型为Date、function、RegExp的时候,a保持不变。
         //总结:
         //我们发现{}或者[]时,简单的赋值操作并不能实现它们的拷贝,只是改了b的指向,使a和b都指向同一个引用,随意改变一个,都会影响另外一个的值。   
    
         {}
         a = {name: 'abc'};b = a;b.name = 'sss'; 
         console.log(a,b)
         // []
         a = ['a','b','c'];b = a;b[1] = 'd'; 
         console.log(a,b)
         // function
         a = function(){ alert('aaa'); };b = a;b = function(){ alert('bbb'); }; 
         console.log(a.toString(),b.toString())
         // Date
         a = new Date('2018-10-11 00:00:00');b = a;b = new Date('1970-01-01 00:00:00');
         console.log(a,b)
         // RegExp
         a = new RegExp('abc');b = a;b = new RegExp('aaa'); 
         console.log(a,b)

    三、深浅拷贝怎样操作(代码示例)

     1.浅拷贝:

     var array = [
            { number: 1 },
            { number: 2 },
            { number: 3 }
         ];
         var copyArray = array.slice();
         copyArray[0].number = 100;
         console.log(array); 
         console.log(copyArray); 
    
    
    
    
         let obj = {
             name: 'Yvette',
             age: 18,
             hobbies: ['reading', 'photography']
         }
         let obj2 = Object.assign({}, obj);
         let obj3 = {...obj};
    
         obj.name = 'Jack';
         obj.hobbies.push('coding');
         console.log(obj);
         console.log(obj2);
         console.log(obj3);
    
    
    
        // 可以看出浅拷贝只最第一层属性进行了拷贝,当第一层的属性值是基本数据类型时,新的对象和原对象互不影响,但是如果第一层的属性值是复杂数据类型,那么新对象和原对象的属性值其指向的是同一块内存

    1)通过Object.assign     for   in  进行浅拷贝 

    //Object.assign 和 for in进行{}和[]的拷贝(浅拷贝--只能对象拷贝一层最外层)
         //Object.assign  对象的复制		
         a = {name: 'aaa'};
         //地址引用
         var b=a;
         // Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
         文件浅拷贝
         var b = Object.assign({},a)
         console.log(a,b)
         b.name = 'bbb';  
         console.log(a,b)
         a = [1,2,3,4,5]; 
         // b=a; 
         b = Object.assign([],a);
         b[1]=9;
        // console.log(a,b)
    
        // for in
    	// 封装的函数  遍历对象或数组  在遍历中赋值给另外个对象
        var copy = function (obj){
           var res =  obj.constructor();
           console.log(res)
           // 遍历对象中的属性及方法
           for(var key in  obj){
             // 判断对象有没有相应的key
             if (obj.hasOwnProperty(key)) {
               // 把对象相应key对象的值赋值给另外一个对象相应的key对应的值
               res[key] = obj[key];
             }
           }
           return  res
        }
    
    
    	
         
         a = {name: 'aaa'};b = copy(a);b.name = 'bbb';  
         console.log(a,b)
         a = [1,2,3];b = copy(a);b[1] = 4;  
         console.log(a,b)
    
       // 浅拷贝只能拷贝一层
       a = {name:'aaa',people:{name: 'abc'}};b = Object.assign({}, a);b.people.name = 'def'; 
         console.log(a,b)                        
       // for in
         var copy = function(a) {
          var res = a.constructor();
          for(var key in a) {
           if(a.hasOwnProperty(key)) {
             res[key] = a[key];
           }
          }
          return res;
         }
       a = {name:'aaa',people:{name: 'abc'}};b = copy(a);b.people.name = 'def'; 
       console.log(a,b)
       a = [1,2, {name: 'aaa'}];b = Object.assign([], a);b[2].name = 'bbb'; 
       console.log(a,b)
       a = [1,2, {name: 'aaa'}];b = copy(a);b[2].name = 'bbb'; 
       console.log(a,b)

     2.深拷贝:

            深拷贝的实现   JSON.parse(JSON.stringify(obj))   完美   递归实现

    1)深拷贝最简单的实现是: JSON.parse(JSON.stringify(obj))

        function deepCopy(obj){
    
                 return JSON.parse(JSON.stringify(obj))
    
               }
    
    
               a = {name:'aaa',people:{name: 'abc'}};b = deepCopy(a);b.people.name = 'def';  
               console.log(a,b)
               a = [1,2, {name: 'aaa'}];b = deepCopy(a);b[2].name = 'bbb';  
               console.log(a,b)
    
               a = {name:'aaa',fun:function(){console.log('fun');},nn: undefined};
               b = deepCopy(a); 
               console.log(a,b)  
    
              // 1.上述的方法会忽略值为function以及undefined的字段,而且对date类型的支持也不太友好
              // 2.上述方法只能克隆原始对象自身的值,不能克隆它继承的值 
               function Person (name) {
                 this.name = name
               }
               var a = new Person('王二');
               var b = deepCopy(a);
               console.log(a.constructor == Person); // true
               console.log(b.constructor == Object); // true

    !!!注意:

      JSON.parse(JSON.stringify(obj)) 是最简单的实现方式,但是有一些缺陷:
         
    对象的属性值是函数时,无法拷贝。
          原型链上的属性无法拷贝
          不能正确的处理 Date 类型的数据
          不能处理 RegExp
          会忽略 symbol
          会忽略 undefined

     2)实现一个 deepClone 函数  (深拷贝,完美)

     // 如果是基本数据类型,直接返回
          // 如果是 RegExp 或者 Date 类型,返回对应类型
          // 如果是复杂数据类型,递归。
          // 考虑循环引用的问题
        var show={
            btn:"btn",
            init:function(){
                var that=this;
                alert(this);
                this.btn.click(function(){
                        that.change();
                        alert(this);
                })            
            },
            change:function(){
                this.btn.css({'background':'green'});
                person={
                  name:"king",
                  show:function(){
                    console.log(this.name)
                  }
                }
            }
        }

    3)递归拷贝

    function deepClone(obj, hash = new WeakMap()) { //递归拷贝
            if (obj instanceof RegExp) return new RegExp(obj);
            if (obj instanceof Date) return new Date(obj);
            if (obj === null || typeof obj !== 'object') {
                //如果不是复杂数据类型,直接返回
                return obj;
            }
            if (hash.has(obj)) {
                return hash.get(obj);
            }
            // *
            //  * 如果obj是数组,那么 obj.constructor 是 [Function: Array]
            //  * 如果obj是对象,那么 obj.constructor 是 [Function: Object]
             
            let t = new obj.constructor();
            hash.set(obj, t);
            for (let key in obj) {
                //递归
                if (obj.hasOwnProperty(key)) {//是否是自身的属性
                    t[key] = deepClone(obj[key], hash);
                }
            }
            return t;
        }
        
        // var show2 = deepClone(show)
        // var show2 =  cloneObject(show)
        console.log(show,show2)
    	
    	// 	//递归函数   笔试或面试最后一道题   使用递归函数实现对象的深拷贝
    		function cloneObject(obj){
    			// 临时对象  暂时存放属性 及属性值
    			var newObj = {}
    			// obj如果是基本数据类型  直接返回
    			if (typeof (obj) !== 'object') {
            return obj;
          }
    			//如果是引用类型,遍历属性
    			else{
            for(var attr in obj){
              // 如果某个属性还是对象 递归调用
              newObj[attr] = cloneObject(obj[attr])
            }
          }
          return newObj;				
    		}
    		

    更多相关内容
  • 面试题:深拷贝和浅拷贝(超级详细,有内存图)

    万次阅读 多人点赞 2019-08-07 13:07:34
    拷贝和浅拷贝是经常在面试中会出现的,主要考察你对基本类型和引用类型的理解深度。我在无数次的面试中,应聘者还没有一个人能把这个问题回答情况,包括很多机构的培训老师。这篇文章会让你把基本类型和引用类型...

    这篇文章竟然写了一上午,亲,请怀着感恩的心阅读!!

     

           深拷贝和浅拷贝是经常在面试中会出现的,主要考察你对基本类型和引用类型的理解深度。我在无数次的面试中,应聘者还没有一个人能把这个问题回答情况,包括很多机构的培训老师。这篇文章会让你把基本类型和引用类型的区别搞得清清楚楚,搞清楚这两者的区别,你对任何编程语言的都不怕,因为,这不是js一门语言,是任何编程语言中都需要掌握的知识,而且,在任何编程语言中,两者都是一样的。

     

    深拷贝和浅拷贝主要是针对对象的属性是对象(引用类型)

    一、基本类型和引用类型的区别

    1、先了解内存

       任何编程语言的内存分区几乎都是一样的

     

           内存是存储数据的,不同类型的数据要存储在不同的区域,即分类存放,不同的区域作用和功能也不一样。就像你家里的衣柜一样,也分了不同的区域:如挂西装的区域,放袜子的区域等等,我相信每个人都会把这两个东西放在不同的区域。要不然,当你西装革履地参加一个高档的宴会,手塞在裤兜里,掏出来一只臭袜子,是不是很尴尬!!!哈哈!!!

     

    以下为内存的分区图。内存分为四个区域:栈区(堆栈),堆区,全局静态区,只读区(常量区和代码区)。

     

    https://blog.csdn.net/jiang7701037/article/details/98728249

     

        2、基本类型和引用类型在内存上存储的区别

    现在只看栈区和堆区,不管其它区域,也假定只是局部变量。

     

    以上函数testf在调用时,

           1)、 定义局部变量 age,由于age是局部变量,所以在栈中申请内存空间,起名为age,又由于给age赋的值250是基本类型,所以,值直接存储在栈中。

           2)、定义局部变量arr,由于arr是局部变量,所以在栈中申请空间,但是arr的内存中存储的是什么?由于给arr赋的值不是基本类型,而是引用类型(new出来的),所以,先在堆中申请空间存放数据 12,23,34,。再把堆区的地址赋给arr。

     

    3、到底什么是基本类型和引用类型

          1)、基本类型:就是值类型,即在变量所对应的内存区域存储的是值,如:上面的age变量所对应的内存存储的就是值250.

          2)、引用类型:就是地址类型。

       何为地址:地址就是编号,要地址何用,就是为了容易找到。每个人的家里为什么要有一个唯一的地址,就是在邮寄时,能够找到你家。

    比如:我们最早的超市存包的格子,每个格子都有个编号,你存包时,服务员会把你的东西放在某个格子里,再把这个格子的编号给你(一个牌子)。你购物完毕取包时,直接给服务员你的牌子(有编号),服务员根据你的编号就会找到你的包。这个编号就是格子的地址。内存也是一样的,每个内存都有一个编号,方便cpu查找。要不然,浩瀚的内存海洋,cpu要找到数据靠啥找。

    以上的变量arr就是引用类型,arr所对应的内存中存储着地址,真正的数据是在地址对应的内存区域里,就像,你填写简历时,会在简历的那张纸上写上你家的地址。简历上写你家地址的地方就相当于arr。而你家是根据这个地址可以找到的。简历上写你家地址的地方就相当于引用着你家(可以想象一根无形的线牵引着你家,在简历上的这根无形的线,顺藤摸瓜就能找到你家)。所以叫做引用类型。

       二、基本类型和引用类型在赋值时内存的变化

    你可以认为,赋值就是在拷贝。

    1、基本类型:

    2、引用类型:

     

    如果给arr[0]赋值的话,arr1[0]的值也会发生变化,因为,arr和arr1保存着相同的地址,它门两个引用的数据是共享的。就像你在很多地方(简历的那张纸,户口本上的那张纸)会写上你的家庭地址。这么多张纸都引用着你家。根据一张纸上找到你家,给你家放上一百万的现金(数据改变了,相当于arr[0]=10),再根据另外一张纸的地址也找到了你家,你发现你一百万在(不要给我说被人拿了)

     

    如果在上面的基础上增加一句代码:arr[0]=10;那么内存将会有如下变化:

    ​​

    三、基本类型和引用类型作为函数参数的区别(这个可以不看)

    1、基本类型作为函数的参数

    2、引用类型作为函数的参数:

    四、深拷贝和浅拷贝:

       终于说到了深拷贝和浅拷贝。

    其实在第二点已经说到了拷贝,所谓拷贝,就是赋值。把一个变量赋给另外一个变量,就是把变量的内容进行拷贝。把一个对象的值赋给另外一个对象,就是把一个对象拷贝一份。

    1、基本类没有问题,

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

           如:

        Var x = 100;

        Var y = x; //此时x和y都是100;

       如果要改变y的值,x的值不会改变。

     

    2、引用类型有问题

      因为,引用类型赋值时,赋的值地址(就是引用类型变量在内存中保存的内容),强烈建议把前面的第二点(基本类型和引用类型在赋值时内存的变化)多看几遍,以保证理解深刻。这样,一劳永逸,以后在碰到任何跟引用类型有关的话题(如:继承时,父类的属性是引用类型)都没有问题。

          如:

    var arr1 = new Array(12,23,34)

    Var arr2 = arr1;//这就是一个最简单的浅拷贝

     

    如果要改变arr2所引用的数据:arr2[0]=100时,那么arr1[0]的值也是100。

            原因就是 arr1和arr2引用了同一块内存区域(以上的第二点中有体现)。

            

    这是最简单的浅拷贝,因为,只是把arr1的地址拷贝的一份给了arr2,并没有把arr1的数据拷贝一份。所以,拷贝的深度不够

             

    3、用json对象的方式(也是引用类型)来演示浅拷贝和深拷贝

       

    1)、定义一个json对象(对象的属性也是对象)

    var p = {
    	"id":"007",
    	"name":"刘德华",
    	"books":new Array("三国演义","红楼梦","水浒传")//这是引用类型
    }

     

    内存图:

     

    2)、把该对象p进行复制一份

    • (一)浅拷贝
    var p2 = {};
    for(let key in p){
    	p2[key] = p[key];	
    }
    p2.books[0] ="四国";
    console.log(p2);
    console.log(p);

    在控制台中打印的结果(p和p2的books[0]都变成了“四国”):

    内存:

     

    • (二)深拷贝(初步)
    var p2 = {};
    for(let key in p){
    	if(typeof p[key]=='object'){
    		p2[key]=[];//因为,我上面写的是数组,所以,暂时赋值一个空数组.
    		for(let i in p[key]){
    			p2[key][i] = p[key][i]
    		}
    	}else{
    		p2[key] = p[key];
    	}
    }
    p2.books[0] ="四国";
    console.log(p2);
    console.log(p);

    在控制台中打印的结果(只有p2的books[0]变成了“四国”)

    内存:

     

      (三)深拷贝(最终)

    3.1、深拷贝_如果属性都是json对象,那么用递归的方式

     

    //如果对象的属性是对象(引用类型),属性的属性也是引用类型,即层层嵌套很多.怎么办,只能递归

    //如下对象,要复制:

    var p = {
    	"id":"007",
    	"name":"刘德华",
    	"wife":{
    		"id":"008",
    		"name":"刘德的妻子",
    		"address":{
    			"city":"北京",
    			"area":"海淀区"
    		}
    	}
    }
    
    //写函数
    function copyObj(obj){
    	let newObj={};
    	for(let key in obj){
    		if(typeof obj[key] =='object'){//如:key是wife,引用类型,那就递归
    			newObj[key] = copyObj(obj[key])
    		}else{//基本类型,直接赋值
    			newObj[key] = obj[key];
    		}
    	}
    	return newObj;
    }
    
    let pNew = copyObj(p);
    pNew.wife.name="张三疯";
    pNew.wife.address.city = "香港";
    console.log(pNew);
    console.log(p);

     

    3.2、深拷贝_如果属性是数组等非键值对的对象

          就得单独处理:要么给数组增加一个自我复制的函数(建议这样做),要么单独判断。

    //给数组对象增加一个方法,用来复制自己
    Array.prototype.copyself = function(){
    	let arr = new Array();
    	for(let i in this){
    		arr[i]  = this[i]
    	}
    	return arr;
    }
    
    var p = {
    	"id":"007",
    	"name":"刘德华",
    	"books":new Array("三国演义","红楼梦","水浒传")//这是引用类型
    }
    
    function copyObj(obj){
    	let newObj={};
    	for(let key in obj){
    		if(typeof obj[key] =='object'){//如:key是wife,引用类型,那就递归
    			newObj[key] = obj[key].copyself();
    		}else{//基本类型,直接赋值
    			newObj[key] = obj[key];
    		}
    	}
    	return newObj;
    }
    
    var pNew = copyObj(p);
    pNew.books[0] = "四国";
    console.log(pNew);
    console.log(p);  

    这篇文章竟然写了一上午,亲,请怀着感恩的心阅读

    展开全文
  • 拷贝与浅拷贝的区别

    千次阅读 2022-05-27 12:19:30
    拷贝与浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是深拷贝(值拷贝)),是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这时,你修改两个中的任意一个都不会影响另一个...

    深拷贝与浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是深拷贝(值拷贝)),是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这时,你修改两个中的任意一个都不会影响另一个;而对于对象或引用数据在进行浅拷贝时,只是将对象的引用复制了一份,也就是内存地址,即两个不同的变量指向了同一个内存地址,那么在改变任意一个变量的值都是改变内存地址所存储的值,因此两个变量的值都会改变。

    1. js中的深拷贝(值拷贝):

    js 中的基本数据类型:String,Number,Boolean,Null,Undefined,在赋值的过程中都是值拷贝。
    例如,let a = 10; b = a ,修改其中一个变量的值,不会影响到另一个变量的值。

    2. js 中的浅拷贝(引用拷贝)

    js 中的对象数据类型:Object,Array,Function,Map,Set,在赋值过程中都是引用拷贝。

    let obj = {	
    	name:'静如秋叶',
    	age: 3,
    	height:100
    }
    let obj2 = obj
    obj2.name = '小花';
    console.log(obj) // {name:'小花',age: 3,height:100}
    console.log(obj2) // {name:'小花',age: 3,height:100}
    

    当修改obj2的name属性时,也会修改obj的name,因为他们指向同一块内存地址。

    3. 将浅拷贝转换为深拷贝

    在实际的项目开发中,我们在多数情况下不希望将对象进行浅拷贝,因为值会相互影响,容易出错。

    3.1 Array的深拷贝

    1. 通过slice方法

    slice() 操作数组时,不会对原数组有影响,会产出一个新的数组。

    let arr1 = [1,42,5,6]
    let arr2 = arr1.slice()
    arr2[0] = 100
    console.log(arr1)  // [1,42,5,6]
    console.log(arr2)  // [100,42,5,6] 
    

    数组arr2的改变未引起arr1的变化。

    2. 通过concat方法

    数组的concat() 方法,能够连接两个数组,同样不会改变原来的数组。
    用一个空数组[ ] 连接另一个数组,即可实现深拷贝。

    let arr3 = ['cat','dog','pig']
    let arr4 = [].concat(arr3)
    arr3[2] = 'big pig'
    console.log(arr3)   // ['cat','dog','big pig']
    console.log(arr4)   // ['cat','dog','pig']
    

    3. 通过ES6语法中的 …

    ES6中的 … ,我们经常在数组的深拷贝中用到。

    let arr5 = [0,0,1]
    let arr6 = [...arr5]
    arr5[0] = 10000
    console.log(arr5) // [10000,0,1]
    console.log(arr6) // [0,0,1]
    

    4. 通过Array.from 方法

    Array.from() 方法能从一个类似数组或可迭代对象中返回一个新的数组实例。通过Array.from() 方法能获取到一个数组的深拷贝。

    let arr7 = [1,2,3]
    let arr8 = Array.from(arr7)
    arr7[1] = 1000
    console.log(arr7) // [1,1000,3]
    console.log(arr8) // [1,2,3]
    

    3.2 Object 的深拷贝

    1. 通过Object.assign() 方法

    ES6的Object.assign(target,…sources) 用于对象的合并,将源对象的所有可枚举属性,复制到目标对象中,并返回合并后的目标对象。后来的源对象的属性值,将会覆盖它之前的对象的属性。

    let person = {
    	name: 'xia',
    	age: 25,
    	height: 160 
    }
    let otherPerson = Object.assign({},person)
    person.age = 30
    console.log(person) // {name: "xia", age: 30, height: 160}
    console.log(otherPerson) // {name: "xia", age: 25, height: 160}
    

    4. 万能转换器(对Array和Object)

    前面讲了Array和Object的深拷贝方法,但是对于有更深层次的结构关系(数组套数组,数组套对象,对象套对象等),上面的方法就失灵了,可以看下面的例子。

    let personArr = [{name: 'xia'}, {name: 'zhang'}]
    let otherPersonArr2 = [...personArr]
    personArr[0].name = 'xia xia'
    console.log(personArr)
    console.log(otherPersonArr2)
    

    在这里插入图片描述
    万能转换器JSON.parse(JSON.stringify(obj)) 深拷贝已对象,它可以深拷贝多层级的,不同担心嵌套问题。

    • JSON.stringify() 将对象序列化成json对象
    • JSON.parse() 反序列化——将json对象反序列化成js对象

    JSON.stringify(obj) 将js中的对象转化为json字符串

    let jack = {
    	name: 'jack'
    }
    console.log(jack)
    console.log(JSON.stringify(jack))
    

    它们在格式上有区别。下图中的第一个是对象,name没有双引号括起来。第二个是json字符串,其中,name用双引号括起来了。
    在这里插入图片描述
    JSON.parse() 将json字符串解析成对象

     let obj = {
         name: '静茹秋叶'
     }
     console.log('obj: ', obj)
     console.log('json string: ', JSON.stringify(obj))
    
     let str = JSON.stringify(obj)
     console.log('--------------')
     console.log(str)
     console.log('str to obj: ', JSON.parse(str))
    

    在这里插入图片描述

    5. Vue中的浅拷贝和深拷贝

    两个button-counter 共用同一个jack对象,用同一块地址,当其中一个实例改变时,会影响另一个实例的值。(浅拷贝)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>vue的data选项</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <button-counter></button-counter>
            <button-counter></button-counter>
        </div>
    
        <script>
            let jack = {
                counter: 0
            }
            // 子组件
            Vue.component('button-counter', {
                data() {
                    // 函数类型
                    return jack
                },
                template: `<button @click="counter++">click {{counter}} times</button>`
            })
            let vm = new Vue({
                el: '#app' // mount到DOM上
            })
        </script>
    </body>
    </html>
    

    采用深拷贝,重新创建一块内存。这样,vue的button-counter组件中的counter值互不影响。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>vue的data选项</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <button-counter></button-counter>
            <button-counter></button-counter>
        </div>
    
        <script>
            let jack = {
                counter: 0
            }
            // 子组件
            Vue.component('button-counter', {
                data() {
                    // 函数类型
                    return JSON.parse(JSON.stringify(jack))
                },
                template: `<button @click="counter++">click {{counter}} times</button>`
            })
            let vm = new Vue({
                el: '#app' // mount到DOM上
            })
        </script>
    </body>
    </html>
    
    展开全文
  • js中的浅拷贝与深拷贝

    千次阅读 2022-02-10 12:16:51
    在前端开发中的过程中我们经常要接触到浅拷贝与深拷贝的问题, 下面就对浅拷贝与深拷贝的概念、区别以及其有哪种实现方法来做一个简单的说明。 概念 浅拷贝: 浅拷贝是指,一个新的对象对原始对象的属性值进行精确地...

    在前端开发中的过程中我们经常要接触到浅拷贝与深拷贝的问题,
    下面就对浅拷贝与深拷贝的概念区别以及其有哪种实现方法来做一个简单的说明。

    概念

    浅拷贝:

    浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是原始数据类型,拷贝的就是原始数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。

    深拷贝:

    深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它会在堆内存空间中创建一个新的实体对象,新的实体对象内容与被拷贝对象的实体对象内容相同,然后新拷贝出来的对象在栈内存中的对象指针就会指向这个新的实体对象,因此拷贝前后的两个对象指向的是两个内存地址不同但内容相同的实体对象。深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败。
    在这里插入图片描述图中obj2是深拷贝obj1得出的对象,两者指向的不是同一个实体对象,obj2指向的是一个在堆内存中新创建的实体对象,并且这个实体对象的内容与原本obj1对象指向的实体对象内容相同。

    区别

    在拷贝原始数据类型的时候其实是不区分深拷贝与浅拷贝的,因为都是拷贝原始数据类型的值。当拷贝的是引用数据类型的时候,则区分浅拷贝、深拷贝,因为浅拷贝只复制引用数据类型的第一层属性、深拷贝可以对引用数据类型的属性进行递归复制

    实现方法

    浅拷贝的实现方法:

    1、Object.assign()

    Object.assign()是ES6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。

    该方法需要注意的是:

    • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
    • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
    • 因为null 和 undefined 不能转化为对象,所以第一个参数不能为null或 undefined,会报错。
    let target = {a: 1};
    let object2 = {b: 2};
    let object3 = {c: 3};
    Object.assign(target,object2,object3);  
    console.log(target);  // {a: 1, b: 2, c: 3}
    

    2、扩展运算符

    使用扩展运算符可以在对对象或数组的第二层开始的拷贝是浅拷贝。

    let obj1 = {a:1,b:{c:1}}
    let obj2 = {...obj1};
    obj1.a = 2;
    console.log(obj1); //{a:2,b:{c:1}}
    console.log(obj2); //{a:1,b:{c:1}}
    obj1.b.c = 2;
    console.log(obj1); //{a:2,b:{c:2}}
    console.log(obj2); //{a:1,b:{c:2}}
    

    3、数组方法实现数组浅拷贝

    (1)Array.prototype.slice (单层深拷贝,多层浅拷贝)

    slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
    该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。

    let arr = [1,2,3,4];
    console.log(arr.slice()); // [1,2,3,4]
    console.log(arr.slice() === arr); //false
    

    (2)Array.prototype.concat (单层深拷贝,多层浅拷贝)

    concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。

    let arr = [1,2,3,4];
    console.log(arr.concat()); // [1,2,3,4]
    console.log(arr.concat() === arr); //false
    

    4、实现浅拷贝函数

    // 浅拷贝的实现;
    
    function shallowCopy(object) {
      // 只拷贝对象
      if (!object || typeof object !== "object") return;
      // 根据 object 的类型判断是新建一个数组还是对象
      let newObject = Array.isArray(object) ? [] : {};
      // 遍历 object,并且判断是 object 的属性才拷贝
      for (let key in object) {
        if (object.hasOwnProperty(key)) {
          newObject[key] = object[key];
        }
      }
      return newObject;
    }
    

    深拷贝的实现方法:

    1、JSON.stringify()

    JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象。
    这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。

    let obj1 = {  a: 0,b: {c: 0}};
    let obj2 = JSON.parse(JSON.stringify(obj1));
    obj1.a = 1;
    obj1.b.c = 1;
    console.log(obj1); // {a: 1, b: {c: 1}}
    console.log(obj2); // {a: 0, b: {c: 0}}
    

    2、函数库lodash的_.cloneDeep方法

    该函数库也有提供_.cloneDeep用来做 Deep Copy

    var _ = require('lodash');
    var obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    var obj2 = _.cloneDeep(obj1);
    console.log(obj1.b.f === obj2.b.f);// false
    

    3、扩展运算符
    使用扩展运算符可以在对对象或数组的第一层的拷贝是深拷贝。

    let obj1 = {a:1,b:{c:1}}
    let obj2 = {...obj1};
    obj1.a = 2;
    console.log(obj1); //{a:2,b:{c:1}}
    console.log(obj2); //{a:1,b:{c:1}}
    obj1.b.c = 2;
    console.log(obj1); //{a:2,b:{c:2}}
    console.log(obj2); //{a:1,b:{c:2}}
    

    4、实现深拷贝函数(解决循环引用)

    // 深拷贝的实现
    function deepCopy(obj, map) {
      //判断是否是第一次调用deepCopy方法,是的话创建一个weakmap实例来装遍历过程中出现过的对象
      if(!map){
    		map = new WeakMap()
    	}
    	//判断传入的obj是否为对象
      if( obj === null || typeof obj !== 'Object' ){
        return obj
      }
      //如果map中已经存在这个对象说明出现了循环引用问题
      if(map.get(obj)){
      	return obj
      }
      //map中没有就往map中存入该对象
      map.set(obj,obj)
      //根据obj的类型来给newObj创建初始值
      let newObj = Array.isArray(obj) ? [] : {}
    	//遍历obj
      for(let i in obj){
    	 if(obj.hasOwnProperty(i)){ //判断当前属性是否为obj自身的属性
    	    if(typeof obj[i] === 'Object'){ //判断当前属性是否为对象类型
    		    newObj[i] = deepCopy(obj[i],map) //如果是对象类型就使用该方法进行递归处理
    	    }else{
    			newObj[i] = obj[i] //不是对象类型就直接拷贝
    		}
    	 }
      }
      return newObj //返回拷贝完成的newObj
    }
    
    展开全文
  • 拷贝和浅拷贝的区别

    万次阅读 多人点赞 2021-08-16 19:38:36
    首先,明确一点深拷贝和浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响...
  • js浅拷贝和深拷贝

    千次阅读 2022-04-12 10:07:40
    1、JS数据类型 基本数据类型:Boolean、String、Number、null、undefined 引用数据类型:Object、Array、Function、RegExp、Date等 ...区别:浅拷贝只复制对象的第一层属性,而深拷贝会对对象的属性进行递归
  • Python 深拷贝和浅拷贝详解

    万次阅读 多人点赞 2021-07-31 16:50:00
    Python深拷贝和浅拷贝详解 浅拷贝,指的是重新分配一块内存,创建一个新的对象,但里面的元素是原对象中各个子对象的引用。 深拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的...
  • Java深入理解深拷贝和浅拷贝区别

    万次阅读 多人点赞 2019-02-13 23:31:47
    一、拷贝的引入 (1)、引用拷贝 创建一个指向对象的引用变量的拷贝。 Teacher teacher = new Teacher("Taylor",26); Teacher otherteacher = teacher; System.out.println(teacher); System.out.println...
  • 什么是零拷贝

    万次阅读 多人点赞 2022-03-24 16:37:49
    拷贝是老生常谈的问题,无论是Kafka还是Netty等都用到了零拷贝的知识,那究竟什么是零拷贝呢 什么是零拷贝 “零”:表示次数是0,它表示拷贝数据的次数是0
  • 详细讲解js中的深拷贝与浅拷贝

    千次阅读 多人点赞 2021-09-05 20:53:02
    拷贝 JSON.parse(JSON.stringify(obj))深拷贝已有对象 JSON.stingify(obj)将js中的对象转换成JSON字符串 let jack = { name: 'jack' } console.log(jack) console.log(JSON.stringify(jack)) 它们在格式上有...
  • Java IO篇:什么是零拷贝

    万次阅读 多人点赞 2022-01-24 02:06:05
    拷贝指在进行数据 IO 或传输时,数据在用户态下经历了零次拷贝,并非不拷贝数据。通过减少数据传输过程中 内核缓冲区和用户进程缓冲区间不必要的 CPU数据拷贝 与 用户态和内核态的上下文切换次数,降低 CPU 在这两...
  • 拷贝和浅拷贝的区别和与原理

    千次阅读 2022-04-20 10:15:51
    拷贝和浅拷贝的区别和与原理
  • python篇 深拷贝与浅拷贝

    千次阅读 2021-12-09 14:30:42
    拷贝拷贝有点类似于这个,“浅”字在这里的意思就是浅浅一层,仅能能拷贝对象的表层,而其子对象,就是直接拿来引用了,所谓深拷贝就是用递归的原理把其子对象也依次拷贝下来,这就是两者的区别。
  • c++拷贝函数——深浅拷贝

    千次阅读 2022-03-19 22:32:01
    拷贝构造函数 拷贝构造函数也是构造函数,长相和构造函数一样的,只是参数是固定 拷贝构造函数唯一的参数是对对象引用 不写拷贝构造函数,也存在一个默认的拷贝构造函数 拷贝构造函数作用: ...
  • C++浅拷贝与深拷贝

    千次阅读 多人点赞 2022-02-11 15:49:15
    拷贝:简单的赋值拷贝操作。 深拷贝:在堆区重新申请空间,进行拷贝操作。 C++中在对一个已知对象进行拷贝的时候,会调用类中的拷贝构造函数,如果程序员未定义拷贝构造函数,则会调用编译器默认的拷贝构造函数...
  • C++ 浅拷贝 & 深拷贝

    千次阅读 多人点赞 2021-05-07 02:19:39
    C++ 对象的赋值, 赋值, 介绍浅拷贝 (shallow copy) 和深拷贝 (deep copy) 的区别.
  • 拷贝和浅拷贝一、如何区分深拷贝和浅拷贝二、举例加深理解深拷贝和浅拷贝三、图文理解四、哪些方法是浅拷贝,如何进行深拷贝 一、如何区分深拷贝和浅拷贝 内在的区别:浅拷贝就是简单的把指向别人的值的一个指针...
  • golang浅拷贝与深拷贝

    千次阅读 2021-04-11 14:37:37
    拷贝 golang中通过copy方法,可以实现浅拷贝操作。 func copy(dst, src []Type) int 基本认识: 1.copy只能用于切片,不能用于 map 等任何其他类型。 2.copy返回结果为一个 int 型值,表示 copy 从原切片src复制...
  • Golang深拷贝拷贝

    万次阅读 2019-05-30 23:19:37
    Golang深拷贝拷贝 在了解原型设计模式之前我们需要新知道Golang的深拷贝与浅拷贝之间的区别。 推荐大家新看看Slice 和 Map那么常见的坑:https://blog.csdn.net/weixin_40165163/article/details/90707593 ...
  • 大多时候时候使用的是Apache或Spring``BeanUtils,今天,我们来看一下一个更高效的属性拷贝方式:BeanCopier。 https://github.com/cglib/cglib ... 首先梳理出来现在有哪些对象拷贝的方式: Apache的BeanUtils:...
  • 【操作系统】:零拷贝

    千次阅读 2022-04-21 12:16:46
    所以拷贝以及零拷贝的学习势在必得,基于此,花了一段时间学习了拷贝和零拷贝。本文文字较多,需要耐心的阅读,当然后期笔者会补上一些示意图,辅助理解。本文主要尝试搞明白的问题有如下几个: 什么是拷贝? 什么...
  • 前端面试:浅拷贝和深拷贝的区别?

    万次阅读 多人点赞 2022-03-08 21:35:46
    拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存;   深拷贝(deep copy):复制并创建一个一摸一样的对象,不共享内存,修改新对象,旧对象保持不变。...
  • C++——深拷贝和浅拷贝

    千次阅读 2022-05-23 21:25:15
    拷贝:将原对象或原数组的引用直接赋给新对象,新数组,新对象/新数组只是原对象的一个引用。 深拷贝:创建一个新的对象和数组,将原对象的各项属性 1.浅拷贝变深拷贝 2.什么时候需要深拷贝,获取堆区资源,...
  • 拷贝是一种高效的数据传输机制,在追求低延迟的传输场景中经常使用。 如果服务端要提供文件传输的功能,最简单的方式是: 1、将磁盘上的文件读取出来 2、通过网络协议将内容发送给客户端 传统IO的工作方式是 ...
  • 当函数的返回值进行返回的时候,它会调用拷贝出一个临时对象,此时返回值就会去拷贝出一个临时对象,由于返回值是一个将亡值,属于一个右值,因此就会调用移动拷贝构造函数拷贝出临时对象,然后临时对象去拷贝出新...
  • 拷贝(英语Zero-copy)技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。零拷贝技术可以减少数据拷贝和共享总线操作的...
  • Python3的深拷贝与浅拷贝

    千次阅读 2021-12-08 22:16:33
    在理解深拷贝和前拷贝之前,首先要了解python的可变类型和不可变类型: 可变类型:list、dict 不可变类型:number、str、bool、tuple python中的不可变数据类型,不允许变量的值 原地 发生变化,一但值发生了变化...
  • BeanUtils深拷贝,浅拷贝

    千次阅读 2022-03-10 14:26:52
    1 深拷贝,浅拷贝拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,948,539
精华内容 779,415
关键字:

怎么拷贝