js深拷貝淺拷貝

目錄

一.數(shù)據(jù)類型

二.淺拷貝與深拷貝

三.賦值和淺拷貝的區(qū)別

四.淺拷貝的實現(xiàn)方式

五.深拷貝的實現(xiàn)方式


一.數(shù)據(jù)類型

數(shù)據(jù)分為基本數(shù)據(jù)類型(String, Number, Boolean, Null, Undefined,Symbol)和對象數(shù)據(jù)類型。
基本數(shù)據(jù)類型:
訪問:基本數(shù)據(jù)類型的值是按值訪問的。
存儲:基本類型的變量是存放在棧內存(Stack)里的。
圖解 : 棧內存中包括了變量的標示符和變量的值。

引用數(shù)據(jù)類型的特點:存儲的是該對象在棧中引用,真實的數(shù)據(jù)存放在堆內存里
引用數(shù)據(jù)類型在棧中儲存了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址后從堆中獲取實體。
訪問:引用類型的值是按引用訪問的。
存儲:引用類型的值是保存在堆內存(Heap)中的對象(Object)。
圖解:
棧內存中包含了變量的標示符和指向堆內存中該對象的指針
堆內存中包含了對象的內容

image

JavaScript 不能直接操作對象的內存空間(堆內存)

一.淺拷貝與深拷貝

深拷貝和淺拷貝是只針對Object和Array這樣的引用數(shù)據(jù)類型的。
深拷貝和淺拷貝的示意圖大致如下:

image

淺拷貝只復制指向某個對象的指針,而不是復制對象本身 新舊對象還是共享同一塊內存。
但深拷貝會另外創(chuàng)造一個一摸一樣的對象, 新對象跟原對象不共享內存,修改新對象不會改到原對象。

三.賦值和淺拷貝的區(qū)別

  • 當我們把一個對象賦值給一個新的變量時,賦的其實是該對象的在棧中的地址,而不是堆中的數(shù)據(jù)。也就是兩個對象指向的是同一個存儲空間,無論哪個對象發(fā)生改變,其實都是改變的存儲空間的內容,因此,兩個對象是聯(lián)動的。

  • 淺拷貝是按位拷貝對象,它會創(chuàng)建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內存地址(引用類型),拷貝的就是內存地址 ,因此如果其中一個對象改變了這個地址,就會影響到另一個對象。即默認拷貝構造函數(shù)只是對對象進行淺拷貝復制(逐個成員依次拷貝),即只復制對象空間而不復制資源。

// 對象賦值
 var obj1 = {
    'name' : 'zhangsan',
    'age' :  '18',
    'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)
image
// 淺拷貝
 var obj1 = {
    'name' : 'zhangsan',
    'age' :  '18',
    'language' : [1,[2,3],[4,5]],
};
 var obj3 = shallowCopy(obj1);
 obj3.name = "lisi";
 obj3.language[1] = ["二","三"];
 function shallowCopy(src) {
    var dst = {};
    for (var prop in src) {
        if (src.hasOwnProperty(prop)) {
            dst[prop] = src[prop];
        }
    }
    return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)
image

上面例子中,obj1是原始數(shù)據(jù),obj2是賦值操作得到,而obj3淺拷貝得到。我們可以很清晰看到對原始數(shù)據(jù)的影響,具體請看下表:


image

四.淺拷貝的實現(xiàn)方式

1.Object.assign()
Object.assign() 方法可以把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,然后返回目標對象。但是 Object.assign()進行的是淺拷貝,拷貝的是對象的屬性的引用,而不是對象本身。

var obj = { a: {a: "xxxc", b: 19} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "wewd";
console.log(obj.a.a); //wewd

注意:當object只有一層的時候,是深拷貝

let obj = {
    username: 'jsx'
    };
let obj2 = Object.assign({},obj);
obj2.username = 'wawa';
console.log(obj);//{username: "jsx"}

2.Array.prototype.concat()

let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);

修改新對象會改到原對象:

example

3.Array.prototype.slice()

let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

修改新對象會改到原對象:


image

Array的slice和concat方法不修改原數(shù)組,只會返回一個淺復制了原數(shù)組中的元素的一個新數(shù)組。
原數(shù)組的元素會按照下述規(guī)則拷貝:

  • 如果該元素是個對象引用(不是實際的對象),slice 會拷貝這個對象引用到新的數(shù)組里。兩個對象引用都引用了同一個對象。如果被引用的對象發(fā)生改變,則新的和原來的數(shù)組中的這個元素也會發(fā)生改變。
  • 對于字符串、數(shù)字及布爾值來說(不是 String、Number 或者 Boolean 對象),slice 會拷貝這些值到新的數(shù)組里。在別的數(shù)組里修改這些字符串或數(shù)字或是布爾值,將不會影響另一個數(shù)組。
let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[1] = 2
console.log(arr,arr3);
image

五.深拷貝的實現(xiàn)方式

1.遞歸方法實現(xiàn)深度克隆原理:遍歷對象、數(shù)組直到里邊都是基本數(shù)據(jù)類型,然后再去復制,就是深度拷貝

var deepCopy = function(obj) {
    if (typeof obj !== 'object') return obj //判斷不是object就直接返回
    var newObj = (Object.prototype.toString.call(obj) === '[object Array]') ? [] : {} //判斷復制的目標是數(shù)組還是對象
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = (typeof obj[key] !== 'object') ? obj[key]: deepCopy(obj[key]) // 如果值是對象,就遞歸一下。 如果不是,就直接賦值
        }
    }
    return newObj
}

就這么簡單。
類似的方法都是挺雷同的

function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 判斷復制的目標是數(shù)組還是對象
  for(let keys in source){ // 遍歷目標
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){ // 如果值是對象,就遞歸一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 如果不是,就直接賦值
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}  

/*原理都一樣*/
var  deepClone=function(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}

2.除此之外還有 函數(shù)庫 lodash
該函數(shù)庫也有提供_.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

JSON.parse(JSON.stringfify(obj))
3.JSON.parse(JSON.stringfify(obj))
原理:
用JSON.stringify將對象轉成JSON字符串,
再用JSON.parse()把字符串解析成對象,一去一來,新的對象產生了,而且對象會開辟新的棧,實現(xiàn)深拷貝。
可以實現(xiàn)數(shù)組或對象深拷貝,但不能處理函數(shù)
因為JSON.stringify() 方法是將一個JavaScript值(對象或者數(shù)組)轉換為一個 JSON字符串,不能接受函數(shù)

可以自行試驗 就不做圖例了,太懶了

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 項目中會遇到的體現(xiàn)。對一個table進行操作后,淺拷貝引用的form一起被改變。深拷貝和淺拷貝簡單解釋淺拷貝和深拷...
    aatter閱讀 423評論 0 0
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,649評論 1 32
  • 什么是深拷貝,什么是淺拷貝 說到深淺拷貝,就不得不提到另外一個知識點,那就是引用類型和基本類型以及堆和棧的區(qū)別。再...
    jeff_nz閱讀 985評論 0 0
  • 先一句話說清楚,深拷貝、淺拷貝探討的是嵌套對象的對象。 接著來看看深拷貝、淺拷貝的異同:1.無論哪種拷貝,原對象和...
    皮卡丘_小面包閱讀 460評論 0 0
  • 坐在寂寞的角落, 你不理我, 我不怪你。 因為我卑微的身份。 不, 誰說農婦的身份就低微, 那是從前的思維。 今天...
    鄉(xiāng)村奶奶閱讀 250評論 4 8

友情鏈接更多精彩內容