作爲程序員,你一定要知道函數底層運行機制!!!

大家好,我是一個函數,一個又累,又苦,又被廣大開發者使用的反派角色。

自從世界各大語言誕生的時候,我也隨之誕生。雖然經常出現在人們的眼前,但是我的出生環境和身世卻很少被人所知。今天不得不借助鹿哥的平臺,進行一次自白。

這麼多年過去了,總是被別人當做“工具人”,卻總是不能夠被人理解,我真的很傷心。多虧遇到了鹿哥,給了我這一次自白的機會。

在各個語言中,雖然我的存在的形態不同,但是我還是我,一個不一樣的煙火,只要被人敲一下代碼,我的兄弟姐妹們就被創建出來。而且我們每個函數天生的命運已經被決定這一生要去做一件事,我們不得不聽天由命。

function fn(){
// 要執行的任務
......
}
function fn(a, b){
  let result = null;
  result = a + b;
  return result;
}

其實,我們也像人類一樣,需要生存的環境,空氣、食物和水,但是我們很少被人所周知。下面我就親自介紹一下我們所生存環境——棧。

我生活的棧內存中,有屬於自己的兩間大房子,一間房子當做倉庫,存放一些變量和變量對應的值。

另一間房子就是我的工作室,執行我天生體內就有的任務代碼。我的每個兄弟姐妹都和我一樣,都有獨立的環境和兩間大房子,只不過我們的命運不同。
在這裏插入圖片描述
在這裏插入圖片描述
每當我的上級 Boss(主線程)告訴我要開始工作了,我就開始在工作室從頭到尾順序執行體內所要處理的任務。

因爲棧內存空間太小,只能存儲一些變量的和值,我體內的任務代碼佔據空間太大,棧內存裏放不下。所以當我出生的時候,我體內的代碼會以一堆破字符串的形式存放在堆內存中(其他大倉庫),我在棧內存中只存這個堆倉庫的地址就行了,這樣需要佔據的棧空間就小很多了。

在這裏插入圖片描述
每當我的 Boss(主線程)告訴我,開始工作了,我就開始在我的存儲變量的棧倉庫和存儲代碼字符串的堆倉庫中,取出所需要執行的值和字符串。先對取出的字符串轉化爲可執行代碼,開始在一個全新私有棧內存環境下執行。將執行完的結果會返回給外界變量兄弟接收。

爲了能讓大家更好的記住我的運行機制,不得不麻煩一下鹿哥把這個函數從定義到執行完畢給梳理一下。

大家好,我是鹿哥。一個迷人帥氣、代碼風騷、文章通俗的硬核男人。以上是函數的自白,它講完了,該換鹿哥 BB 幾句了。

我們啓動運行程序,程序代碼自上而下順序執行,當遇到下面代碼時,會發生什麼呢?

function fn(a, b){
  let result = null;
  result = a + b;
  return result;
}
var a = fn(10, 10)
console.log(a)

首先,遇到定義的函數 fn 時,會開闢一個棧內存空間,上邊函數自己說了,棧內存空間會分爲兩部分,一部分是存儲變量和對應的值。另一部分會分配給主線程用來執行代碼。

在這裏插入圖片描述
由於函數是引用類型,會開闢一個堆內存空間,函數體內的所有執行代碼字符串全部存放在堆中,當函數執行的時候才能叫做代碼,這個時候只是存儲在堆內的一堆破字符串而已。

在這裏插入圖片描述
程序繼續自上而下執行,會將 a 變量存儲在棧內存中,但是它對應的值就是函數執行後的返回值。

var a = fn(10, 10)

函數開始執行,每一次函數執行的目的就是把函數體內的代碼(先從堆內存中取出那一堆破字符串,然後轉化爲代碼)執行。而且是在一個全新私有的棧內存內執行。

函數體內的代碼開始自上而下順序執行,這個全新的私有作用域棧和之前我們說到的棧內存是一樣的,也是由兩部分組成。

首先將代碼中的變量推入到棧中,然後根據代碼的運算取出相對應的變量進行求值,最後 retrun,將計算的值返回給變量 a,此時 a 在棧內存中有了對應的值。

在這裏插入圖片描述

PS:如果函數體內有 return 則直接返回 return 的值,如果沒有,則返回的是 undefined。

以上就是整個函數從定義到執行最後結束的底層過程,但是我們還沒有更深入,我們上邊說到了棧中執行的兩部分專業名詞叫做 VO 階段和 AO 階段,再深入就會涉及到 JS 的編譯過程,又可以作爲一大部分去講解。

通過上邊函數底層的運行機制,我們有很多疑問得到解惑,比如爲什麼函數內聲明的變量,函數外部不可訪問?

因爲函數執行,又重新創建了一個全新私有作用域,外界是不能直接訪問該私有作用域棧內存中的值的,只有通過 return 的方式把值返回出去或者通過某種方式暴露出去。

函數定義的時候,函數體不執行的原因?

定義函數時,讓對應的變量指向該堆內存的地址。我們只不過是在堆內存存了一堆破字符串而已,而不是代碼,無法執行。只有當調用函數的時候,從堆內存中取出字符串轉化爲可執行的代碼,函數體纔可以執行。

通過這篇函數底層的運行機制,我們之前存在的一些對函數的疑問都一目瞭然。雖然像這種函數的底層運行機制看起來不難,但是我們自學中很難去梳理把握全面。

一些大廠的面試題,雖然看起來表面感覺很複雜,如果通過鹿哥上述對代碼的整個底層運作拆解,再複雜的題目對你來說也會遊刃有餘。

不信來個阿里的面試題,歡迎在留言區寫出你的答案和分析的過程。

 let a = {
    n:1
 }
 let b = a;
 
 a.x = a = {
    n:2
 }
 console.log(a.x)
console.log(b)

❤️ 不要忘記留下你學習的腳印 [點贊 + 收藏 + 評論]

1、老鐵們,我是小鹿,歡迎關注我的原創微信公衆號 「 小鹿動畫學編程」,專注於數據結構和算法,網絡原理,計算機基礎、Web 大前端等,歡迎來鹿哥公衆號搞事情。一天一篇動畫家屬技術搞定你。。

2、給俺點個讚唄,可以讓更多的人看到這篇文章,順便激勵下我,就這麼臉皮厚!

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