深拷贝与浅拷贝理解及拷贝方法

  在说深拷贝、浅拷贝之前,首先先说一下js里存在的两种数据类型:基本数据类型和引用数据类型。

- 基本数据类型有:boolean Null Undefined Number String Symbol
- 引用类型有:Object(包含 Array、Function、RegExp、Date等等)

区别:基本数据类型名值都在栈内存中,而引用数据类型的名存在栈内存中,值存在堆内存中,但是栈内存会提供一个引用地址指向堆内存中的值。

  因为JS不允许直接访问堆内存,也不能直接对堆内存进行操作,所以,引用类型的值是按引用访问的。


  • 深拷贝 & 浅拷贝

  深拷贝、浅拷贝是专门针对于引用数据类型的一种概念,因为对于引用数据类型的名、值存在不同的内存空间当中,所以当发生拷贝行为的时候,系统会开辟一块新的栈内存空间,存放的是被复制的变量在栈内存中的值,即堆内存中的值的引用地址。

  即新的变量复制得到的是被复制对象的引用地址,当变量发生改变值的操作时,指向该对象的地址的其他变量的值也会随之变化,此为浅拷贝。

  当然,我们有时候并不希望这样的场景发生,所以这时候就需要用到深拷贝的方法去进行拷贝。

堆、栈内存.jpg


  • 深拷贝 & 浅拷贝常用方法
    • 浅拷贝方法:
      ( = 号只是引用,并没有发生拷贝行为,在内存中并没有开辟新的空间!!!)

      1. slice() // 操作对象:数组
      
          let a = [1,2,3,"4",{name:"lme"}];
          let b = a.slice(0);
          console.log("a: ",a); // [1,2,3,"4",{name:"lme"}]
          console.log("b: ",b); // [1,2,3,"4",{name:"lme"}]
      
          b[4].name = "dsg";
          console.log("修改b,打印a: ",a); // [1,2,3,"4",{name:"dsg"}]
      
      2. concat() // 操作对象:数组
      
          let a = [1,2,3,"4",{name:"lme"}];
          let b = a.concat();
          console.log("a: ",a); // [1,2,3,"4",{name:"lme"}]
          console.log("b: ",b); // [1,2,3,"4",{name:"lme"}]
      
          b[4].name = "cool";
          console.log("修改b,打印a: ",a); // [1,2,3,"4",{name:"cool"}]
      
      3. Array.from() // 操作对象:数组
      
          let a = [1,2,3,"4",{name:"lme"}];
          let b = Array.from(a);
          console.log("a: ",a); // [1,2,3,"4",{name:"lme"}]
          console.log("b: ",b); // [1,2,3,"4",{name:"lme"}]
      
          b[4].name = "cool";
          console.log("修改b,打印a: ",a); // [1,2,3,"4",{name:"cool"}]
          
      4. ...操作符 // 操作对象:数组 && 对象
      
          let a = [1,2,3,"4",{name:"lme"}];
          let b = [...a];
          console.log("a: ",a); // [1,2,3,"4",{name:"lme"}]
          console.log("b: ",b); // [1,2,3,"4",{name:"lme"}]
      
          b[4].name = "cool";
          console.log("修改b,打印a: ",a); // [1,2,3,"4",{name:"cool"}]
          
          let c = {age:18,sex:1,child:{name:"小明",age:2,sex:1}};
          let d = {...c};
          console.log("c: ", c); // {age:18,sex:1,child:{name: "小明",age: 2,sex: 1}}
          console.log("d: ", d); // {age:18,sex:1,child:{name: "小明",age: 2,sex: 1}}
      
          d.child.name = "小红";
          console.log("修改d,打印c: ", c); // {age:18,sex:1,child:{name: "小红",age: 2,sex: 1}}
      
      5. Object.assign() // 操作对象:对象
      
          let c = {age:18,sex:1,child:{name:"小明",age:2,sex:1}};
          let d = Object.assign({},c);
          console.log("c: ", c); // {age:18,sex:1,child:{name: "小明",age: 2,sex: 1}}
          console.log("d: ", d); // {age:18,sex:1,child:{name: "小明",age: 2,sex: 1}}
      
          d.child.name = "小红";
          console.log("修改d,打印c: ", c); // {age:18,sex:1,child:{name: "小红",age: 2,sex: 1}}
      
    • 深拷贝方法:

      1. // 当所拷贝的数组或对象的一维元素或属性都是 基本数据类型 的时候  
         // 可以使用以上浅拷贝的方法去达成一次深拷贝的结果。
         slice()、concat、Array.from()、...操作符、Object.assign()
         // 注意:当且仅当 数组或对象的一维元素或者属性全部都是 基本数据类型 的时候
         // 拷贝结果才是深拷贝,否则为浅拷贝!!!
      
      2. JSON.parse(JSON.stringify(obj))  // 序列化 -> 反序列化
         // 将数组或对象序列化转成字符串,然后再反序列化转成对象,由于字符串是String类型,
         // 属于基本类型,所以会切断与源对象引用地址的联系而形成一个新的对象。
      
         // 该方法可以对多维对象进行深拷贝,但是需要注意的是,在序列化时,某些特定的值或者类型将会丢失
      
         如下:
          1. Date时间对象 
      
             // 如果obj里的某一条属性的值为时间对象,则序列化之后再反序列化得到的结果,
             // 时间将只是字符串的形式存在,而不是时间对象。
      
          2. RegExp、Error对象   
      
             // 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象。
      
          3. 函数、undefined 
      
             // 如果obj里有函数、undefined,则序列化的结果会把函数或undefine丢失。
      
          4. NaN 
             // 如果obj里有NaN,则序列化的结果会变成null
      
          5. constructor 
             // 如果obj中的某一条属性的值是由构造函数生成的对象,则使用
             // JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor构造函数。
             // 比如:
                  function Person(name) {
                      this.name = name;
                  }
          
                  let lme = new Person('lme');
          
                  let a = {
                      name: 'a',
                      data: lme,
                  };
      
                  let b = JSON.parse(JSON.stringify(a));
      
                  console.log("a",a); // 构造函数:Person类型
                  console.log("b",b); // 构造函数:无
      
                  b.name = "gyj";
                  b.data.name = "gyj is lme's wife"
      
                  console.log("a1: ",a) // 原始a对象没有被修改
                  console.log("b1: ",b) // b对象被修改
      
                // 感兴趣的同学可以打印一下看看呦~
      
          PS: 该方法简单粗暴,适用于简单的引用类型数据,使用前请三思~
      
      3. 自行编写函数进行递归遍历复制,实现深拷贝。
      
         // 先根据要拷贝的变量的类型声明一个新的变量,用for in遍历要拷贝的变量,然后判断如果当前
         // 循环中的key(属性)的值是一个对象,那么便递归,对该属性继续进行遍历拷贝,否则直接将
         // 该属性的值赋值给新的对象的属性。
      
         // 如果需要将原型链上继承过来的属性过滤掉的话,可使用hasOwnProperty(),该方法会判断传
         // 入的参数是否在对象上,如果是原型链上的属性或方法,则会返回false。
      
         例:
            function deepCopy(obj) {
              let newObj = obj.constructor === Array ? [] : {} 
      
              for (let key in obj) { // Array实际上也是一个对象,即也是引用数据类型
                typeof obj[key] === 'object' ? newObj[key] = deepCopy(obj[key]) : newObj[key] = obj[key]
              }
      
              return newObj
            }
      
      
      



(限于本人技术有限,本文如有表述不当的地方,欢迎赐教~)
(转载到其他平台请包含本文的简书链接或说明出处~)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342