引用類型和基本類型區別(哈哈)
今天明白了一個困擾很久的問題:引用類型和基本類型的區別與聯系
要明白這個問題,首先需要理解堆棧的概念。那什麽又是堆棧,有什麽區別和聯系呢?
堆:首先堆是動態分配的,JVM並不會自動釋放這部分內存。只用等待系統的gc來進行內存回收。
棧:是在類加載中有系統靜態分配的,而且分配時按照內存的高低地址分配。這部分內存系統會自動進行釋放。
string是一個特殊類型,它存儲的機制是引用類型。
以下文章非本人所寫。來源:http://www.cnblogs.com/jeffchen/archive/2006/12/28/605689.html
堆(Heap)棧(Stack)
1、內存分配方面:
堆:一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。註意它與數據結構中的堆是兩回事,分配方式是類似於鏈表。可能用到的關鍵字如下:new、malloc、delete、free等等。
棧:由編譯器(Compiler)自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
2、申請方式方面:
堆:需要程序員自己申請,並指明大小。在c中malloc函數如p1 = (char *)malloc(10);在C++中用new運算符,但是註意p1、p2本身是在棧中的。因為他們還是可以認為是局部變量。
棧:由系統自動分配。 例如,聲明在函數中一個局部變量 int b;系統自動在棧中為b開辟空間。
3、系統響應方面:
堆:操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣代碼中的delete語句才能正確的釋放本內存空間。另外由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。
棧:只要棧的剩余空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。
4、大小限制方面:
堆:是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
棧:在Windows下, 棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是固定的(是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。
5、效率方面:
堆:是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便,另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活。
棧:由系統自動分配,速度較快。但程序員是無法控制的。
6、存放內容方面:
堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。
棧:在函數調用時第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧,然後是函數中的局部變量。 註意: 靜態變量是不入棧的。當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
7、存取效率方面:
堆:char *s1 = "Hellow Word";是在編譯時就確定的;
棧:char s1[] = "Hellow Word"; 是在運行時賦值的;用數組比用指針速度要快一些,因為指針在底層匯編中需要用edx寄存器中轉一下,而數組在棧上直接讀取
基本類型和引用類型(http://hi.baidu.com/www492337/blog/item/1d3e5df1eb0734d60b46e079.html) 2011-11-21 17:00
按照CEMA-262第3版的定義,JS變量松散類型的本質,決定了它只是在特定時間用於保存特定值的一個名字而已。由於不存在必須定義變量數據類型的規則,變量的值及其數據類型可以在腳本生命周期內改變。盡管從某種角度看,這可能是一個既有趣又強大,同時又容易出問題的特性,但js變量實際的復雜程度還遠不止如此。
ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。 基本類型值指的是那些保存在棧內存中的簡單數據段,即這種值完全保存在內存中的一個位置。 而引用類型值則是指那些保存在堆內存中的對象,意思是變量中保存的實際上只是一個指針,這個指針指向內存中的另一個位置,該位置保存對象。
在將一個值賦給變量時,解析器必須確定這個值是基本類型值,還是引用類型值。(Undefined、Null、Boolean、Number、String)這五種基本數據類型的值在內存中分別占有固定大小的空間,因此可以把他們的值保存在棧內存中。對於保存基本數據類型的變量,我們可以說他們是按值訪問的,因為我們操作的是它們實際保存的值。
如果賦給變量的是一個引用類型的值,則必須在堆內存中去為這個值分配空間。由於這種值的大小不固定,因此不能把它們保存到棧內存中。但內存地址的大小是固定的,因此可以將內存地址保存在棧內存中。這樣,當查詢引用類型的變量時,就可以首先從棧中讀取內存地址,然後在“順藤摸瓜”找到保存在堆中的值。對於這種查詢方式,我們把它叫做按引用訪問,因為我們操作的不是實際的值,而是被那個值所引用的對象。 定義基本類型值和引用類型值的的方式是類型的。但是對不同類型值可以執行的操作則是大相徑庭。對於引用類型的值 ,我們可以為其添加屬性和方法,也可以改變和刪除其屬性和方法。 var person = new Object(); person.name = "Nicholas"; alert(person.name); //"Nicholas" 以上代碼創建了一個對象並將其保存在了變量person中。然後,為該對象添加了一個名為name的屬性,並將“Nicholas”賦給這個屬性。緊接著又通過alert()訪問了這個新屬性。如果對象不被銷毀或這個屬性不被刪除,則這個屬性將一直存在。
2 復制變量值 除了保存方式不同之外,在一個變量向另一個變量復制基本類型值和引用類型值時,也存在不同。 如果從一個變量向另一個變量復制基本類型的值,會在棧中創建一個新值,然後把該值復制到為新變量分配的位置上。 var num1 = 5; var num2 = num1; 這2個“5”是完全獨立的,它們可以參與任何操作而不會相互影響。
當一個變量向另一個變量復制引用類型的值時,同樣也會將存儲在棧中的值復制一份到為新變量分配的空間中。不同的是,這個值的副本實際上是一個指針,而這個指針指向存儲在堆中的一個對象。復制後,兩個變量實際上將引用同一個對象。因此改變其中一個變量,就會影響到另一個變量: var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); // "Nicholas"
3 傳遞參數 ECMAScript 中所有函數的參數都是按值傳遞的。也就是說,把函數外部的值復制給函數內部參數,就和把值從一個變量復制到另一個變量一樣。基本類型值傳遞如同基本類型變量的復制一樣,而引用類型值的傳遞,則如同引用類型變量的復制一樣。
在向參數傳遞基本類型的值時,被傳遞的值會被復制給另一個局部變量(即命名參數,或用ECMAScript的概念來說,就是arguments對象中的一個元素)。
function addTen(num){
var count = 20;
在向參數傳遞引用類型的值時,會把這個值在內存中的地址復制給一個局部變量,因此這個局部變量的變化會反應在函數的外部。
function setName(obj){
var person = new Object();
為了證明對象是按值傳遞的,我們看看下面這個修改過的例子:
function setName(obj){
var person = new Object(); 這個例子與前面唯一的區別,就是在setName()中重新定義了一個對象並為該對象定義了一個帶有不同值的name屬性。 如果 person 是按引用傳遞的,那麽person 就會自動被修改為指向其name屬性值為“Greg”的對象。但是,當接下來在訪問person.name時,顯示的值仍然是“Nichoas”。 這說明即使在函數內部修改了參數的值,但原始的引用仍然保持未變。實際上,當在函數內部重寫obj時,這個變量引用的就是一個局部對象了。而這個局部對象會在函數執行完畢後立即被銷毀。 可以把CEMAScript函數的參數想象成局部變量。
4 檢測類型 在檢測基本數據類型時typeof是非常得力的助手,但在檢測引用類型的值時,這個操作符的用處不大。通常,我們並不是想知道某個值是對象,而是想知道他是什麽類型的對象。為此,ECMAScript提供了instanceof操作符 result = variable instanceof constructor 如果 變量是給定引用類型(由構造函數表示)的實例,那麽instanceof操作符就會返回true:
alert(person instanceof Object); //變量person 是Object嗎? alert(colors instanceof Array); //變量person 是Array嗎? alert(pattern instanceof RegExp); //變量person 是RegExp嗎?
根據 規定,所有引用類型的值都是Object的實例。因此,在檢測一個引用類型值和Object構造函數時,instanceof 操作符始終會返回true。 當然,如果使用instenceof操作符檢測基本類型值,返回false.因為基本類型不是對象
使用typeof檢測函數時,該操作會返回"function".在Safari 和 Chrome中使用 typeof檢測正則時,會錯誤地返回"function" |
--------------------- 本文來自 Charles_HJT 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/qq_35224673/article/details/51752774?utm_source=copy
引用類型和基本類型區別(哈哈)