JS專題之深淺拷貝

前言

在開發過程當中,偶爾會遇到這種場景,拿到一個數據後,你打算對它進行處理,可是你又但願拷貝一份副本出來,方便數據對比和之後恢復數據。javascript

那麼這就涉及到了 JS 中對數據的深淺拷貝問題,所謂深淺拷貝,淺拷貝的意思就是,你只是複製了對象數據的引用,並無把內存裏的值另外複製一份,那麼深拷貝就是把值完整地複製一份新的值。java

在以前的文章《JS專題之數據類型和類型檢測》中我有講過,JS 中的數據類型分爲兩種,基本數據類型和引用數據類型,基本數據類型是保存在棧的數據結構中的,是按值訪問,因此不存在深淺拷貝問題。jquery

而好比對象,數組,函數,正則,時間對象這些都是引用數據類型,是保存在堆中的。因此,引用數據類型的複製,是內存地址的傳遞,並無拷貝出一份新的數據。數組

那麼深拷貝,淺拷貝的區別是什麼呢?先給結論:數據結構

操做拷貝以後的數據不會影響到原數據的值拷貝,就是深拷貝,反正,有影響則爲淺拷貝。閉包

1、應用場景

平常開發中,JS 拷貝大多會在 數據保存,數據比對,數據同步 時出現,因此,當你在這些場景的時候,要記得裏面隱藏有一個數據深淺拷貝的問題。app

2、淺拷貝

咱們來看一下淺拷貝:函數

function clone(origin) {
    var result = {};
        for (var prop in origin) {
            if (origin.hasOwnProperty(prop)) {
                result[prop] = origin[prop];
            }
        }
        return result;
}

var jay = {
    name: "jayChou",
    age: 40,
    family: {
        wife: "Quinlivan"
    }
}

var otherJay = clone(jay);

otherJay.age = 18;
otherJay.family.wife = "otherGirl";

console.log(jay); 
// 
// {
// name: "jayChou",
// age: 40, // 沒被改變
// family: {
// wife: "otherGirl" // 同時被改變,說明是同一個引用
// }
// }

console.log(otherJay);

// 
// {
// name: "jayChou",
// age: 18,
// family: {
// wife: "otherGirl" // 被改變了
// }
// }
複製代碼

咱們發現,首先,淺拷貝不是直接賦值,淺拷貝新建了一個對象,而後將源對象的屬性都一一複製過來,複製的是值,而不是引用。工具

咱們知道,對象都是按地址引用進行訪問的,淺拷貝的複製只複製了第一層的屬性,並無遞歸將全部的值複製過來,因此,操做拷貝數據,對原數據產生了影響,故而爲淺拷貝。post

進而,那些能夠直接返回原數組的方法就能夠簡單實現數組和對象淺拷貝。

// 一、 數組淺拷貝 - slice
function shallowCopy1(origin) {
    return origin.slice();
}

// 二、 數組淺拷貝 - concat
function shallowCopy2(origin){
    return origin.concat();
}

// 三、 數組淺拷貝 - 遍歷
function shallowCopy3(origin){
  var result = [];
  for(var i = 0; i < origin.length; i++) {
    result.push(origin[i]);
  }
  return result;
}


// 四、 對象淺拷貝 - Object.assign
function shallowCopy4(origin) {
    return Object.assign({},origin)
}

// 五、 對象淺拷貝 - 擴展運算符
// 擴展運算符(...)用於取出參數對象的全部可遍歷屬性,拷貝到當前對象之中
function shallowCopy5(origin) {
    return {
        ...origin
    }
}

複製代碼

Object.assign 的拷貝,假如源對象的屬性值是一個指向對象的引用,它也只拷貝那個引用值。MDN 有相應的實例和解釋。

2、深拷貝

深拷貝就完整複製數據的值(而非引用),目的在於避免拷貝後數據對原數據產生影響。

目前深拷貝的實現方法主要有遞歸複製,JSON 正反序列化:

// 1. 深拷貝 - JSON 正反序列化
// 缺點就是沒法拷貝 undefined、function、symbol 這類特殊的屬性值。
function deepClone1(origin) {
    return JSON.parse(JSON.stringify(arr));
}

// 2. 深拷貝 - 遞歸;
function deepClone2(origin) {
  const result = origin.constructor === Array ? [] : {};
  for (let keys in origin) {
    // 不遍歷原型鏈上的屬性
    if (origin.hasOwnProperty(keys)) {
      if (origin[keys] && typeof origin[keys] === "object") {
        // 若是值是對象,就遞歸一下, 區分是通常對象仍是數組對象
        result[keys] = origin[keys].constructor === Array ? [] : {};
        // 若是是引用數據類型,會遞歸調用
        result[keys] = deepClone(origin[keys]);
      } else {
        // 若是不是,就直接賦值
        result[keys] = origin[keys];
      }
    }
  }
  return result;
}

複製代碼

JS 的深拷貝的應用,須要根據你的使用場景進行使用,首先是有無必要深拷貝,其次是數據類型,是否直接使用 JSON 的 API 其實就能夠。

JS 深淺拷貝在平常開發中使用頻率仍是較高的,其中考察的知識點,主要在於:

一、是否遇到過深淺拷貝的問題,裏面有什麼坑
二、是否瞭解 JS 的數據類型,數據在計算機中的存儲機制
三、是否瞭解數組、對象的一些經常使用的 API
四、jquery、lodash、underscore 的相關工具函數使用

總結

深淺拷貝主要考察了開發者對 JS 數據類型的瞭解,數組,對象經常使用方法的特色和應用,遞歸函數的封裝。

春節快樂!
寫於大年三十,不寫文章渾身不舒服~ 歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。

掘金專欄 JavaScript 系列文章

  1. JavaScript之變量及做用域
  2. JavaScript之聲明提高
  3. JavaScript之執行上下文
  4. JavaScript之變量對象
  5. JavaScript之原型與原型鏈
  6. JavaScript之做用域鏈
  7. JavaScript之閉包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值傳遞
  11. JavaScript之例題中完全理解this
  12. JavaScript專題之模擬實現call和apply
  13. JavaScript專題之模擬實現bind
  14. JavaScript專題之模擬實現new
  15. JS專題之事件模型
  16. JS專題之事件循環
  17. JS專題之去抖函數
  18. JS專題之節流函數
  19. JS專題之函數柯里化
  20. JS專題之數組去重
相關文章
相關標籤/搜索