賦值、淺拷貝、深拷貝區別

文章首次發表於 我的博客,系列文章目錄:github.com/funnycoders…前端

數據類型存儲

基本類型數據保存在在棧內存中 引用類型數據保存在堆內存中,引用數據類型的變量是一個指向堆內存中實際對象的引用,存在棧中。git

爲何基本數據類型存在棧內存,引用數據類型存在堆內存?github

  • 基本數據類型比較妥當,相對來講佔用的內存較小
  • 引用數據類型是動態的,大小不固定,佔用的內存較大,但內存地址大小是固定的,所以能夠將內存地址保存在棧中

淺拷貝和賦值(=)的區別

基本類型賦值,系統會爲新的變量在棧內存中分配一個新值,這個很好理解。 引用類型賦值,系統會爲新的變量在棧內存中分配一個值,這個值僅僅是指向同一個對象的引用,和原對象指向的都是堆內存中的同一個對象。面試

var obj1 = new Object();
var obj2 = obj1;
obj1.name = 'lucyStar';
console.log(obj2.name);
// lucyStar
複製代碼

咱們能夠看到,obj1保存了一個對象的實例,這個值被賦值到 Obj2中。賦值操做完成後,兩個變量實際引用的是同一個對象,改變了其中一個,會影響另一個值。數組

什麼是淺拷貝? 若是是對象類型,則只拷貝一層,若是對象的屬性又是一個對象,那麼此時拷貝的就是此屬性的引用。微信

簡單實現一個淺拷貝markdown

function shadowCopy(obj) {
    const newObj = {};
    for(let prop in obj) {
        if(obj.hasOwnProperty(prop)){
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
}
const obj1 = {
    name: 'litterStar',
    a: {
        b: '1'
    }
};
const obj2 = shadowCopy(obj1);
obj2.name = 'lucyStar';
obj2.a.b = '2';
console.log(obj1);
// { name: 'litterStar', a: { b: '2' } }
console.log(obj2);
// { name: 'lucyStar', a: { b: '2' } }
複製代碼

能夠看到修改obj2name 屬性不會影響 obj1,可是修改 obj2的 a屬性(是個對象)的 b,就會影響 obj1.a.b函數

使用下面這些函數獲得的都是淺拷貝:post

  • Object.assign
  • Array.prototype.slice(), Array.prototype.concat()
  • 使用拓展運算符實現的複製

深拷貝

什麼是深拷貝? 淺拷貝是隻拷貝一層,深拷貝會拷貝全部的屬性。深拷貝先後兩個對象互不影響。學習

深拷貝的實現

  • JSON.parse(JSON.stringify())
  • 手寫遞歸函數
  • 函數庫lodash

JSON.parse(JSON.stringify())有存在如下問題:

  • 沒法解決循環引用問題
  • 沒法拷貝特殊的對象,好比:RegExp, Date, Set, Map等在序列化的時候會丟失。
  • 沒法拷貝函數

嘗試本身寫一個深拷貝,須要考慮下面這幾種狀況

  1. 屬性是基本類型
  2. 屬性是對象
  3. 屬性是數組
  4. 循環引用的狀況,好比 obj.prop1 = obj
function deepCopy(originObj, map = new WeakMap()) {
    // 判斷是否爲基本數據類型
    if(typeof originObj === 'object') {
        // 判斷是都否爲數組
        const cloneObj = Array.isArray(originObj) ? [] : {};
        // 判斷是否爲循環引用
        if(map.get(originObj)) {
            return map.get(originObj);
        }
        map.set(originObj, cloneObj);
        for(const prop in originObj) {
            cloneObj[prop] = deepCopy(originObj[prop], map);
        }
        return cloneObj;
    } else {
        return originObj;
    }
}

const obj1 = {
    a: '111',
}
obj1.obj2 = obj1;

const aa = deepCopy(obj1);
console.log(aa);
// { a: '111', obj2: [Circular] }
複製代碼

上面只是實現一個簡單的深拷貝,不少狀況未考慮到,好比 特殊的數據類型及兼容性的處理,更多細節的實現能夠參考 lodash 中的 cloneDeep方法。

總結

和原數據是否指向同一對象 第一層數據爲基本數據類型 原數據中包含對象
賦值 改變會使原數據一同改變 改變會使原數據一同改變
淺拷貝 改變不會使原數據一同改變 改變會使原數據一同改變
深拷貝 改變不會使原數據一同改變 改變不會使原數據一同改變

備註:markdown 表格在 tableconvert.com/ 該網站生成,很方便。

參考

其餘

最近發起了一個100天前端進階計劃,主要是深挖每一個知識點背後的原理,歡迎關注 微信公衆號「牧碼的星星」,咱們一塊兒學習,打卡100天。

相關文章
相關標籤/搜索