深拷貝與淺拷貝理解及拷貝方法

??在說深拷貝、淺拷貝之前呵哨,首先先說一下js里存在的兩種數據類型:基本數據類型和引用數據類型。

- 基本數據類型有:boolean Null Undefined Number String Symbol
- 引用類型有:Object(包含 Array、Function袱饭、RegExp、Date等等)

區(qū)別:基本數據類型名值都在棧內存中呛占,而引用數據類型的名存在棧內存中虑乖,值存在堆內存中,但是棧內存會提供一個引用地址指向堆內存中的值晾虑。

??因為JS不允許直接訪問堆內存疹味,也不能直接對堆內存進行操作仅叫,所以,引用類型的值是按引用訪問的糙捺。


  • 深拷貝 & 淺拷貝

??深拷貝诫咱、淺拷貝是專門針對于引用數據類型的一種概念,因為對于引用數據類型的名洪灯、值存在不同的內存空間當中坎缭,所以當發(fā)生拷貝行為的時候,系統(tǒng)會開辟一塊新的棧內存空間签钩,存放的是被復制的變量在棧內存中的值掏呼,即堆內存中的值的引用地址。

??即新的變量復制得到的是被復制對象的引用地址铅檩,當變量發(fā)生改變值的操作時憎夷,指向該對象的地址的其他變量的值也會隨之變化,此為淺拷貝昧旨。

??當然拾给,我們有時候并不希望這樣的場景發(fā)生,所以這時候就需要用到深拷貝的方法去進行拷貝臼予。

堆鸣戴、棧內存.jpg


  • 深拷貝 & 淺拷貝常用方法
    • 淺拷貝方法:
      ( = 號只是引用,并沒有發(fā)生拷貝行為粘拾,在內存中并沒有開辟新的空間U!g止汀)

      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()
         // 注意:當且僅當 數組或對象的一維元素或者屬性全部都是 基本數據類型 的時候
         // 拷貝結果才是深拷貝暇咆,否則為淺拷貝7孀Α!爸业!
      
      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遍歷要拷貝的變量镜粤,然后判斷如果當前
         // 循環(huán)中的key(屬性)的值是一個對象捏题,那么便遞歸,對該屬性繼續(xù)進行遍歷拷貝肉渴,否則直接將
         // 該屬性的值賦值給新的對象的屬性公荧。
      
         // 如果需要將原型鏈上繼承過來的屬性過濾掉的話,可使用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閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異甸祭,居然都是意外死亡缕碎,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門池户,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咏雌,“玉大人,你說我怎么就攤上這事校焦∩薅叮” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵寨典,是天一觀的道長氛雪。 經常有香客問我,道長耸成,這世上最難降的妖魔是什么报亩? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮井氢,結果婚禮上弦追,老公的妹妹穿的比我還像新娘。我一直安慰自己花竞,他們只是感情好劲件,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著左胞,像睡著了一般寇仓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烤宙,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天遍烦,我揣著相機與錄音,去河邊找鬼躺枕。 笑死服猪,一個胖子當著我的面吹牛,可吹牛的內容都是我干的拐云。 我是一名探鬼主播罢猪,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叉瘩!你這毒婦竟也來了膳帕?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎危彩,沒想到半個月后攒磨,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡汤徽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年娩缰,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谒府。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拼坎,死狀恐怖,靈堂內的尸體忽然破棺而出完疫,到底是詐尸還是另有隱情泰鸡,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布壳鹤,位于F島的核電站鸟顺,受9級特大地震影響,放射性物質發(fā)生泄漏器虾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一蹦锋、第九天 我趴在偏房一處隱蔽的房頂上張望兆沙。 院中可真熱鬧,春花似錦莉掂、人聲如沸葛圃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽库正。三九已至,卻和暖如春厘唾,著一層夾襖步出監(jiān)牢的瞬間褥符,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工抚垃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喷楣,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓鹤树,卻偏偏與公主長得像铣焊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子罕伯,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355