JavaScript中有一些您作為開發人員可能不知道的主題。了解這些主題可以幫助您編寫更好的代碼。其中包括內存生命周期、堆、棧和調用棧。在本教程中,您將了解這些主題以及JavaScript的工作原理。
JavaScript是一種非常寬容的編程語言。它允許你以多種方式做很多事情。它也為你做了很多工作。內存管理就是其中之一。問問你自己:多少次你不得不考慮為你的變量或函數分配內存?
當你不再需要那些變量或函數時,你需要考慮釋放這些內存多少次?機會不止一次。這同樣適用于了解堆、堆棧和調用堆棧的工作方式,或者它到底是什么。然而,您仍然可以使用JavaScript。您仍然可以編寫每天都能工作的代碼。
這些事情你不必知道。它們也不是必需的。但是,了解它們以及它們如何工作可以幫助您理解JavaScript是如何工作的。反過來,這可以幫助您編寫更好的代碼,成為更好的JavaScript。
記憶的生命周期
讓我們從最簡單的部分開始。什么是內存生命周期,它是關于什么的,它是如何在JavaScript中工作的?內存生命周期是指編程語言如何使用內存。無論哪種語言,內存的生命周期幾乎總是相同的。它由三個步驟組成。
第一步是內存分配。當您分配一個變量或創建一個函數或對象時,必須為它分配一定數量的內存。第二步是內存使用。當您使用代碼中的數據進行讀或寫操作時,您使用的是內存。從變量中讀取或改變值就是從內存中讀取和寫入內存。
第三步是釋放記憶。當你不再使用某個函數或對象時,內存就會被釋放。一旦釋放,就可以再次使用。這就是對內存生命周期的概括。JavaScript的優點是它為您實現了這三個步驟。
JavaScript根據需要分配內存。它使您更容易處理分配的內存。最后,它還能起重和清理所有的臟亂。它使用垃圾收集來持續檢查內存,并在不再使用時釋放內存。結果呢?
作為一名JavaScript開發人員,您不必擔心為變量或函數分配內存。在讀取之前,您也不必擔心選擇正確的內存地址。而且,你不必擔心釋放你過去使用過的內存。
堆棧和內存堆
現在您已經了解了內存生命周期的各個步驟。你知道內存的分配,使用和釋放。你可能會問,這些變量、函數和對象實際存儲在哪里?答案是:視情況而定。JavaScript不會將所有這些東西存儲在同一個地方。
相反,JavaScript使用了兩個位置。這些位置是堆棧和內存堆。將使用這些位置中的哪一個取決于您當前正在使用的內容。
堆棧
堆棧是JavaScript僅用于存儲靜態數據的地方。這包括基本數據類型值。例如,數字、字符串、布爾值、未定義和null。這些靜態數據還包括引用。這些引用指向您創建的對象和函數。
這些數據有一個共同點。這些數據的大小是固定的,JavaScript在編譯時就知道這個大小。這也意味著JavaScript知道它應該分配多少內存,并分配這個數量。這種類型的內存分配稱為“靜態內存分配”。它發生在代碼執行之前。
關于靜態數據和內存,有一點很重要。這些原始值的大小是有限制的。對于堆棧本身也是如此。這也有局限性。這些限制有多高取決于特定的瀏覽器和引擎。
內存堆
JavaScript存儲數據的第二個地方是內存堆。這種存儲更加動態。當涉及到內存堆時,JavaScript并不分配固定數量的內存。相反,它在需要的時候分配內存。這種類型的內存分配被稱為“動態內存分配”。
哪些數據存儲在內存堆中?堆棧是JavaScript存儲靜態數據的地方,而內存堆是JavaScript存儲對象和函數的地方。記住,使用原語創建時,使用的是靜態數據。JavaScript將這些靜態數據存儲在堆棧中。
這些數據總是固定分配內存。另一方面,當您創建對象或函數時,JavaScript將它們存儲在內存堆中。為這些分配的內存不是固定的。它是根據需要動態分配的。
當您創建一個變量并為它賦值時,它將存儲在堆棧中。當你做同樣的嘗試時,會發生一些不同的事情,但是是針對一個對象。如果您聲明一個變量并為它分配一個對象,將會發生兩件事。首先,JavaScript將在堆棧中為該變量分配內存。
當涉及到對象本身時,JavaScript將把它存儲在內存堆中。存在于堆棧中的那個變量將只指向內存堆中的這個對象。該變量將是對該對象的引用。您可以將引用看作是現有事物的快捷方式或別名。
這些參考不是那些東西本身。它們只是那些“真實”事物的鏈接。您可以使用這些鏈接來訪問它們引用(它們鏈接到)的內容并對其進行操作。
復制對象和原語
這也是為什么在JavaScript中創建對象副本并不是那么簡單的原因。試圖通過引用來創建存儲在變量中的對象的副本不會創建真正的副本。它不會復制對象本身。它只復制引用那個對象。這叫做淺拷貝。
當您更改原始對象時,副本也將更改。這是因為仍然只有一個對象。但是,對那個對象有兩個引用(別名或鏈接)。當您使用其中一個引用來更改對象時,另一個引用仍然指向相同的對象,即您剛剛更改的對象。
當您嘗試復制原始值時,這將不會發生。當您嘗試復制原始值,并且更改了原始值時,副本將保持不變。原因是:沒有參考資料。您正在創建真正的副本,并直接使用這些副本。
您可能已經聽說過所謂的“調用堆棧”。這與我們之前在本教程中討論的堆棧不同。如您所知,堆棧是JavaScript用來存儲用原始值賦值的變量的地方。調用堆棧是不同的。
調用堆棧是JavaScript用來跟蹤函數的一種機制。當您調用一個函數時,JavaScript會將該函數添加到調用堆棧中。如果該函數調用另一個函數,JavaScript也會將該函數添加到調用堆棧中,位于第一個函數之上。
這個過程將對上一個函數調用的任何其他函數重復。當一個函數完成時,JavaScript將從調用堆棧中刪除該函數。有兩件重要的事情。第一件事是堆棧中的每個新函數都將被添加到調用堆棧的頂部。
第二件事是調用堆棧從上到下執行。添加到堆棧的最后一個函數將作為first執行。添加到堆棧的第一個函數將最后執行。這也被稱為后進先出原則。讓我們用一個簡單示例的代碼來說明這一點。
總結:JavaScript中的內存生命周期、堆、棧和調用棧
內存生命周期、堆、堆棧和調用堆棧是不經常討論的主題。沒有太多的材料可以用來更多地了解它們。我希望本教程能幫助您理解內存生命周期、堆、堆棧和調用堆棧是什么,以及它們是如何工作的。
如果你喜歡這篇文章,請訂閱,這樣你就不會錯過任何未來的帖子。