好程序員web前端教程分享引用類型與基本類型

  好程序員web前端教程分享引用類型與基本類型本文將從以下六個方面講解引用類型和基本類型

  1. 概念

  2. 內存圖

  3. 引用類型和基本類型作爲函數的參數體現的區別

  4. 引用類型的優點:

  5. 引用類型的賦值(對比基本類型)

  6. 淺拷貝和深拷貝

  以下爲詳細內容:

  1. 概念:

  基本類型也叫簡單類型,存儲的數據是單一的,如:學生的個數就是一個數字而已;引用類型也叫複雜類型,存儲的數據是複雜的,如:學生,包括學號,姓名,性別,年齡等等很多信息。從內存(大家如果不懂內存,請查閱相關資料)的角度來說:基本類型只佔用一塊內存區域;引用類型佔用兩塊內存區域。即定義基本類型的變量時,在內存中只申請一塊空間,變量的值直接存放在該空間;定義引用類型的變量時(容易理解的是,我門看到new運算符,一般就是定義引用類型的變量),在內存中申請兩塊空間,第一塊空間存儲的是第二塊空間的地址,第二塊空間存儲的是真正的數據;第一塊空間叫作第二塊空間的引用(地址),所以叫作引用類型。

  javaScript中的基本類型包括:數字(Number),字符串(String),布爾(Boolean),Null,Undefined五種;

  javascript的引用類型是:Object。而Array,Date是屬於Obejct類型。

  2. 內存圖:

  如下代碼(都是定義了兩個局部變量):

  function demoFun(){

  var num = 20;//定義了一個基本類型的變量。

  var arr = new Array(12,23,34);//定義了一個引用類型的變量

  }

以上兩行代碼的內存圖:

  可以看到,num變量只佔用了一塊內存區域;arr變量佔用了兩塊內存區域,arr變量在棧區(不懂棧區的人,先不要想太多)申請了一塊內存區域,存儲着地址,存儲的地址是堆區的地址。而堆區中真正才存儲着數據,所以說,arr變量佔用了兩塊內存區域。這樣看來,引用類型的變量好像還佔用內存多了。哈哈,不要着急,後面瞭解了引用類型的優點後,你就會覺得這是問題了。

  當我們讀取num變量的值時,直接就能讀到,但是當我們要讀取arr裏的值時,先找到arr中的地址,然後根據地址再找到對應的數據。

  引用類型,類似於windows操作系統中的快捷方式。快捷方式就是一個地址,真正的內容是快捷方式所指向的路徑的內容。如:我們把d:\t.txt文件創建一個快捷方式放在桌面上,那麼,桌面上的快捷方式會佔用桌面的空間,而d:\t.txt會佔用d盤的空間,所以,佔用了兩塊空間。

  基本類型就相當於文件。

  引用類型,類似於我們在入學報名填寫報名表時,填寫家庭地址,這個家庭地址就相當於第一塊空間,真正你家(第二塊內存空間)不在報名表上。學校要找你家,先在報名表上找到你家的地址,然後根據地址,才能找到你家去。

  3. 引用類型的優點:

  引用類型作爲函數的參數時,優點特別明顯,第一,形參傳遞給實參時,只需要傳遞地址,而不需要搬動大量的數據(節約了內存開銷);第二,形參對應的數據改變時,實參對應的數據也在改變(很多時候,我們希望這樣)。

  如以下代碼:

  先定義函數(冒泡排序)

  function bubble(arr){

  for(var i=0;i

  for(var j=0;j

  if(arr[j]>arr[j+1]){

  var temp = arr[j];

  arr[j] = arr[j+1];

  arr[j+1] = temp;

  }

  }

  }

  }

  當調用冒泡排序時,

  var arr1 = [250,2,290,35,12,99];

  bubble(arr1);

  看看內存以上代碼執行時的,內存變化:

  圖中,當執行,①對應的代碼(var arr1 = [250,2,290,35,12,99];)時,內存中會產生①對應的變化,即在棧中申請一塊內存區域,起名爲arr1,在堆區中申請內存空間放置250,2,290,35,12,99,並把堆區中的內存的地址賦給arr1的內存中;當執行②對應的代碼bubble(arr1)時,調用函數。這時候會定義形參arr(內存中③對應的變化),即在棧中申請一塊內存區域,起名爲arr,並把arr1保存的地址賦給了arr(內存中②表示的賦值),這樣,形參arr和實參arr1就指向了同一塊內存區域。數組中的值250,2,290,35,12,99在內存中只有一份。即,不用把數組中每個元素的值再複製一份,節約了內存。如果對內存圖看懂了,那麼,當形參arr對應的數據順序改變了,實參arr1對應的數據順序也就改變了。即,實現了形參數據改變時,實參數據也改變了。所以,bubble函數不需要返回值,依然可以達到排序的目的。可以運行我示例中的代碼,看看是不是達到了排序的效果。

  補充,基本類型作爲函數參數的內存變化:

  內存圖:

  4. 引用類型變量的賦值:

  引用類型變量賦值時,賦的是地址。即兩個引用類型變量裏存儲的是同一塊地址,也就是說,兩個引用類型變量都指向同一塊內存區域。所以,兩個引用類型變量對應的數據時一樣的。

  再如:

  var person1 = {

  name:"張三",

  sex:"男",

  age:12

  };

  var person2 = person1;

  person2.name="張四"; //這句話會改變person1和person2的name。說明person1和person2的name佔用的是同一塊內存。

  alert(person1.name+","+person1.sex+","+person1.age);

  alert(person2.name+","+person2.sex+","+person2.age);

  基本類型變量賦值時的內存變化。

  5. 淺拷貝和深拷貝

  先說對象的複製,上面說了,引用類型(對象)的賦值,只是賦的地址,那麼要真正複製一份新的對象(即克隆)時,又該怎麼辦。

  var person1 = {

  name:"張三",

  sex:"男",

  age:12

  };

  var person2={};

  for(var key in person1){

  person2[key] = person1[key];

  }

  但是,當一個對象的屬性又是一個引用類型時,會出現淺拷貝和深拷貝的問題。用一個自定義的object類型來說明問題。

  如:

  var person1 = {

  name:"張三",

  sex:"男",

  age:12,

  address:{

  country:"陝西",

  city:"渭南"

  }

  };

  //對象person1的address又是個對象,即,要對person1做真正的克隆,需要把address中的每個屬性也進行克隆。

  var person2={};

  for(var key in person1){

  person2[key] = person1[key];

  }

  person2.name="張四"; //不會改變掉person1的name屬性。

  person2.address.country="北京";//會改變掉person1的address.country

  大家注意看,person1和person2的name屬性各有各的空間,但是person1.address.country和person2.address.country是同一塊空間。所以,改變person2.address.country的值時,person1.address.country的值也會改變。這就說明拷貝(克隆)的不到位,這種拷貝叫作淺拷貝,而進一步把person1.address.country和person1.address.name也拷貝(克隆)了,就是深拷貝。要做到深拷貝,就需要對每個屬性的類型進行判斷,如果是引用類型,就再循環進行拷貝(需要用到遞歸)。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章