深入理解深拷貝與淺拷貝

2021-10-01 06:43:19 字數 3740 閱讀 1472

深拷貝和淺拷貝是經常在面試中會出現的,主要考察你對基本型別和引用型別的理解深度。我在無數次的面試中,應聘者還沒有乙個人能把這個問題回答情況,包括很多機構的培訓老師。這篇文章會讓你把基本型別和引用型別的區別搞得清清楚楚,搞清楚這兩者的區別,你對任何程式語言的都不怕,因為,這不是js一門語言,是任何程式語言中都需要掌握的知識,而且,在任何程式語言中,兩者都是一樣的。
深拷貝和淺拷貝主要是針對物件的屬性是物件(引用型別)

一、基本型別和引用型別的區別

1、先了解記憶體

任何程式語言的記憶體分割槽幾乎都是一樣的

記憶體是儲存資料的,不同型別的資料要儲存在不同的區域,即分類存放,不同的區域作用和功能也不一樣。就像你家裡的衣櫃一樣,也分了不同的區域:如掛西裝的區域,放襪子的區域等等,我相信每個人都會把這兩個東西放在不同的區域。要不然,當你西裝革履地參加乙個高檔的宴會,手塞在褲兜裡,掏出來乙隻臭襪子,是不是很尷尬!!!哈哈!!!
以下為記憶體的分割槽圖。記憶體分為四個區域:棧區(堆疊),堆區,全域性靜態區,唯讀區(常量區和**區)。

2、基本型別和引用型別在記憶體上儲存的區別
現在只看棧區和堆區,不管其它區域,也假定只是區域性變數。

以上函式testf在呼叫時,

1)、 定義區域性變數 age,由於age是區域性變數,所以在棧中申請記憶體空間,起名為age,又由於給age賦的值250是基本型別,所以,值直接儲存在棧中。

2)、定義區域性變數arr,由於arr是區域性變數,所以在棧中申請空間,但是arr的記憶體中儲存的是什麼?由於給arr賦的值不是基本型別,而是引用型別(new出來的),所以,先在堆中申請空間存放資料 12,23,34,。再把堆區的位址賦給arr。

3、到底什麼是基本型別和引用型別

1)、基本型別:就是值型別,即在變數所對應的記憶體區域儲存的是值,如:上面的age變數所對應的記憶體儲存的就是值250.
比如:我們最早的超市存包的格仔,每個格仔都有個編號,你存包時,服務員會把你的東西放在某個格仔裡,再把這個格仔的編號給你(乙個牌子)。你購物完畢取包時,直接給服務員你的牌子(有編號),服務員根據你的編號就會找到你的包。這個編號就是格仔的位址。記憶體也是一樣的,每個記憶體都有乙個編號,方便cpu查詢。要不然,浩瀚的記憶體海洋,cpu要找到資料靠啥找。

以上的變數arr就是引用型別,arr所對應的記憶體中儲存著位址,真正的資料是在位址對應的記憶體區域裡,就像,你填寫簡歷時,會在簡歷的那張紙上寫上你家的位址。簡歷上寫你家位址的地方就相當於arr。而你家是根據這個位址可以找到的。簡歷上寫你家位址的地方就相當於引用著你家(可以想象一根無形的線牽引著你家,在簡歷上的這根無形的線,順藤摸瓜就能找到你家)。所以叫做引用型別。

二、基本型別和引用型別在賦值時記憶體的變化

你可以認為,賦值就是在拷貝。

1、基本型別:

2、引用型別:

如果給arr[0]賦值的話,arr1[0]的值也會發生變化,因為,arr和arr1儲存著相同的位址,它門兩個引用的資料是共享的。就像你在很多地方(簡歷的那張紙,戶口本上的那張紙)會寫上你的家庭位址。這麼多張紙都引用著你家。根據一張紙上找到你家,給你家放上一百萬的現金(資料改變了,相當於arr[0]=10),再根據另外一張紙的位址也找到了你家,你發現你一百萬在(不要給我說被人拿了)

如果在上面的基礎上增加一句**:arr[0]=10;那麼記憶體將會有如下變化:

三、基本型別和引用型別作為函式引數的區別(這個可以不看)

1、基本型別作為函式的引數

2、引用型別作為函式的引數:

四、深拷貝和淺拷貝:

終於說到了深拷貝和淺拷貝。

其實在第二點已經說到了拷貝,所謂拷貝,就是賦值。把乙個變數賦給另外乙個變數,就是把變數的內容進行拷貝。把乙個物件的值賦給另外乙個物件,就是把乙個物件拷貝乙份。

1、基本類沒有問題,

因為,基本型別賦值時,賦的是資料(所以,不存在深拷貝和淺拷貝的問題)。

如:

var x = 100;

var y = x; //此時x和y都是100;

如果要改變y的值,x的值不會改變。

2、引用型別有問題

因為,引用型別賦值時,賦的值位址(就是引用型別變數在記憶體中儲存的內容),強烈建議把前面的第二點(基本型別和引用型別在賦值時記憶體的變化)多看幾遍,以保證理解深刻。這樣,一勞永逸,以後在碰到任何跟引用型別有關的話題(如:繼承時,父類的屬性是引用型別)都沒有問題。

如:
var arr1 = new array(12,23,34)

var arr2 = arr1;//這就是乙個最簡單的淺拷貝

如果要改變arr2所引用的資料:arr2[0]=100時,那麼arr1[0]的值也是100。

原因就是 arr1和arr2引用了同一塊記憶體區域(以上的第二點中有體現)。
這是最簡單的淺拷貝,因為,只是把arr1的位址拷貝的乙份給了arr2,並沒有把arr1的資料拷貝乙份。所以,拷貝的深度不夠

3、用json物件的方式(也是引用型別)來演示淺拷貝和深拷貝

1)、定義乙個json物件(物件的屬性也是物件)

var p =

記憶體圖:

2)、把該物件p進行複製乙份

(一)淺拷貝

var p2 = {};

for(let key in p)

p2.books[0] =「四國」;

console.log(p2);

console.log§;

在控制台中列印的結果(p和p2的books[0]都變成了「四國」):

記憶體:(二)深拷貝(初步)

var p2 = {};

for(let key in p)

}else

}p2.books[0] =「四國」;

console.log(p2);

console.log§;

在控制台中列印的結果(只有p2的books[0]變成了「四國」)

記憶體:(三)深拷貝(最終)

3.1、深拷貝_如果屬性都是json物件,那麼用遞迴的方式

//如果物件的屬性是物件(引用型別),屬性的屬性也是引用型別,即層層巢狀很多.怎麼辦,只能遞迴

//如下物件,要複製:

var p = }}

//寫函式

function copyobj(obj);

for(let key in obj)else

}return newobj;

}let pnew = copyobj§;

pnew.wife.name=「張三瘋」;

pnew.wife.address.city = 「香港」;

console.log(pnew);

console.log§;

3.2、深拷貝_如果屬性是陣列等非鍵值對的物件

就得單獨處理:要麼給陣列增加乙個自我複製的函式(建議這樣做),要麼單獨判斷。
//給陣列物件增加乙個方法,用來複製自己

array.prototype.copyself = function()

return arr;

}var p =

function copyobj(obj);

for(let key in obj)else

}return newobj;

}var pnew = copyobj§;

pnew.books[0] = 「四國」;

console.log(pnew);

console.log§;

IOS 深入理解 深拷貝 與 淺拷貝

oc 陣列中的深拷貝與淺拷貝 dog dog1 dog new 這裡就是淺拷貝,即指標拷貝 dog dog2 dog1 dog.h inte ce dog nsobject property nonatomic nsinteger age enddog.m implementation dog id...

js中的淺拷貝深拷貝深入理解

舉個例子來說明一下什麼是淺拷貝什麼是深拷貝 var x c 1,2,3 var y shallow x 得出的結果可以看出是淺拷貝 出現這種情況的本質是 物件是按引用賦值的 指的是拷貝乙個物件,改變乙個值不影響另乙個的值 實現深複製功能 判斷 1.是否是物件 2.是否是函式 function dee...

深入淺出理解 深拷貝 淺拷貝

百科定義 拷貝就是拷貝指向物件的指標,意思就是說 拷貝出來的目標物件的指標和源物件的指標指向的記憶體空間是同一塊空間,淺拷貝只是一種簡單的拷貝,讓幾個物件公用乙個記憶體,然而當記憶體銷毀的時候,指向這個記憶體空間的所有指標需要重新定義,不然會造成指標錯誤。基本型別 undefined,null,bo...