玩轉(zhuǎn)JS中的堆棧內(nèi)存及函數(shù)底層處理機(jī)制

我們都知道 JS 都可以運(yùn)行在瀏覽器中,我們還知道它是一門(mén)弱類(lèi)型,基于原型的動(dòng)態(tài)腳本,那么它是不是只能在瀏覽器中運(yùn)行呢吭从?
答案是不是的,如今的JS已經(jīng)強(qiáng)大到不止瀏覽器這些平臺(tái)運(yùn)行了恶迈,還可以在Node環(huán)境涩金,WebView中運(yùn)行谱醇,這些都是基于我們強(qiáng)大的V8引擎所賜,賦予了 JS 脫離瀏覽器也可以運(yùn)行的能力步做。

那么 JS 又是如何在瀏覽器等其他平臺(tái)運(yùn)行的呢副渴?

這涉及到編譯原理,js在剛開(kāi)始就是一大坨字符串文本全度,瀏覽器中的解釋器(編譯器)會(huì)對(duì)這些字符串有序地進(jìn)行詞法解析,語(yǔ)法解析后生成AST(抽象語(yǔ)法書(shū)),最后將AST轉(zhuǎn)換成可執(zhí)行的代碼就是解釋器的最后一步工作代碼生成煮剧。

從 JS 腳本加載到執(zhí)行的過(guò)程中會(huì)有3個(gè)巨佬圍著他,讓他順利運(yùn)行且執(zhí)行将鸵,咚咚咚勉盅,他們分別就是引擎,解釋器以及作用域

由于這些和我們的主題雖然有關(guān)聯(lián)顶掉,但是不是我們主題重點(diǎn)了解的知識(shí)點(diǎn)草娜,我們就點(diǎn)到為止,有機(jī)會(huì)繼續(xù)深究學(xué)習(xí)痒筒,望解宰闰,共勉,嘻嘻~

本文主要講解的是 JS 運(yùn)行過(guò)程中的堆棧內(nèi)存變化以及函數(shù)底層的處理機(jī)制

That's right! 這篇文章我們重點(diǎn)學(xué)習(xí)的知識(shí)點(diǎn)就是理解什么是ECStack,EC,VO,AO以及GO

通過(guò)上文我們了解到 JS 之所以可以在瀏覽器運(yùn)行是因?yàn)闉g覽器給它提供了供代碼運(yùn)行的環(huán)境凸克。
代碼在運(yùn)行前瀏覽器會(huì)分配一個(gè)內(nèi)存供代碼執(zhí)行议蟆,而這個(gè)內(nèi)存我們稱(chēng)之為ECStack,我們也稱(chēng)之為執(zhí)行棧

ECStack (Execution Content Stack)

ECStack 是計(jì)算機(jī)分配的一塊內(nèi)存萎战,專(zhuān)門(mén)供代碼執(zhí)行咐容。

EC (Execution Content)

代碼執(zhí)行 又可以分為全局代碼,函數(shù)中代碼蚂维,私有塊代碼等等戳粒,不同環(huán)境下的代碼執(zhí)行都會(huì)有自己的上下文,這些上下文就是EC虫啥,我們也可以稱(chēng)之為執(zhí)行上下文蔚约,也可以稱(chēng)之為當(dāng)前上下文環(huán)境

VO(Varibale Object)

VO 就是變量對(duì)象涂籽,代碼在當(dāng)前上下文執(zhí)行時(shí)創(chuàng)建的變量總是會(huì)存儲(chǔ)在當(dāng)前上下文中指定的變量對(duì)象中 苹祟,簡(jiǎn)單地說(shuō)就是變量對(duì)象就是用來(lái)儲(chǔ)存當(dāng)前上下文創(chuàng)建的創(chuàng)建的變量。

AO (Active Object)

其中评雌,很多童靴再學(xué)習(xí) VO 后在了解 AO 可能就會(huì)被弄暈树枫,不僅會(huì)浮現(xiàn)一個(gè)問(wèn)題 -- VOAO 有什么瓜系?景东?砂轻?

當(dāng)初靚靚的筆者在初學(xué)的時(shí)候也有過(guò)這個(gè)問(wèn)題,迷惑了我好久斤吐,但是其實(shí)AO 可以理解為是 VO 的一種搔涝,區(qū)別就是 AO 是在函數(shù)中的 變量對(duì)象厨喂,名曰 AO,
又可以叫它為 二狗子 活動(dòng)對(duì)象

這就有趣了庄呈,為什么說(shuō)是同一個(gè)概念蜕煌,到函數(shù)中就變了呢?

其實(shí)我們?cè)趧?chuàng)建函數(shù)的過(guò)程中诬留,函數(shù)內(nèi)部的代碼不管對(duì)還是錯(cuò)我們都可以成功加載到頁(yè)面中幌绍,因?yàn)槲覀冞€沒(méi)有調(diào)用執(zhí)行它,也就是我們聲明一個(gè)函數(shù)時(shí)故响,你又沒(méi)有調(diào)用它傀广,那么它就失去了它的作用,相當(dāng)于內(nèi)部?jī)?chǔ)存的代碼塊那么和筆者一樣靚也沒(méi)有用彩届,就一坨字符串而已伪冰。

相反,如果我們創(chuàng)建它樟蠕,而且還有責(zé)任心地調(diào)用執(zhí)行它贮聂,實(shí)現(xiàn)了它的價(jià)值,高興地和用釘釘上課的孩子一樣寨辩,然后在內(nèi)部創(chuàng)建一個(gè) AO吓懈,用來(lái)儲(chǔ)存體內(nèi)創(chuàng)建的變量,然后會(huì)綁定作用域鏈scope-chain靡狞,<EC(FUNC),EC(G)> ,鏈的左端是當(dāng)前上下文EC(FUNC)耻警,鏈的右側(cè)就是函數(shù)創(chuàng)建時(shí)所處的上下文,也叫函數(shù)的作用域 EC(G)

作用域鏈

PS: <EC(FUNC),EC(G)> 只是用來(lái)參考甸怕,以便理解8蚀!梢杭!

顧名思義温兼,作用域鏈就是一條連接多個(gè)不同的作用域的鏈,就和我們的原型鏈一樣武契,開(kāi)發(fā)的時(shí)候如果雙鏈齊用募判,哪怕別人學(xué)會(huì)了葵花寶典都打不過(guò)你了。

更為具體地來(lái)說(shuō)咒唆,作用域鏈就是當(dāng)前私有上下文代碼執(zhí)行届垫,遇到一個(gè)變量,首先會(huì)問(wèn)私有上下文中AO是否創(chuàng)建過(guò)這么一個(gè)變量钧排,如果有的話(huà)就自給自足敦腔,接下來(lái)的操作都在于私有操作均澳,和其他上下文則沒(méi)有任何關(guān)系恨溜。如果沒(méi)有呢符衔?不是自己私有變量的話(huà)就會(huì)通過(guò)作用域鏈走向上級(jí)的上下文中查找,如果找到了就取它糟袁,沒(méi)有的話(huà)就重復(fù)繼續(xù)往在上級(jí)的上下文查找判族,直到訪(fǎng)達(dá)到全局上下文EC(G),如果找到了就取它项戴,如果取不到就看是什么操作形帮,假如是獲取值的話(huà)找不到也沒(méi)有用方法,只能給你來(lái)一句 “梁非凡,……** is not define周叮;但是如果是設(shè)置的話(huà)辩撑,就會(huì)在全局對(duì)象 GO (下文會(huì)有解釋?zhuān)?中創(chuàng)建一個(gè)變量,并且掛載到 window,和window中對(duì)應(yīng)的屬性變量呈現(xiàn)一種映射的關(guān)系仿耽。

同時(shí)函數(shù)執(zhí)行的過(guò)程中合冀,會(huì)生成一個(gè)封閉,私有的上下文项贺,外界無(wú)法訪(fǎng)問(wèn)君躺,用于保護(hù)里面的變量不受外界干擾,我們把這種函數(shù)執(zhí)行的這種保護(hù)機(jī)制稱(chēng)之為閉包开缎。

GO (Global Object)

文章剛開(kāi)始的時(shí)候我們提及到瀏覽器會(huì)分配一塊內(nèi)存用來(lái)執(zhí)行 JS 代碼棕叫,就是我們的 ECStack,同時(shí)也虧開(kāi)辟一個(gè)堆內(nèi)存奕删,存儲(chǔ)一些內(nèi)置的方法以及屬性俺泣,例如我們的setTimeout,setInterval,isNaN等內(nèi)置屬性和方法,然后瀏覽器會(huì)在全局變量對(duì)象中創(chuàng)建一個(gè)變量在指向全局對(duì)象完残,也就是我們的 GO砌滞,這個(gè)變量就是window,在 Node 環(huán)境中的與全局對(duì)象掛鉤的變量是 global坏怪。

最后不得不強(qiáng)調(diào)一點(diǎn)贝润,全局對(duì)象 !== 全局變量對(duì)象,這兩個(gè)含義完全不一樣铝宵。

紙上談兵終覺(jué)淺打掘,絕知此事要躬行。我準(zhǔn)備了一些簡(jiǎn)單的題目來(lái)更加直觀地理解上述的名詞性知識(shí)點(diǎn)鹏秋,深入淺出供童靴食用更佳尊蚁。

?? ?? ??

var a = 1;
var b = a;
    
b = 3;
    
console.log(a) // 1

為什么變量 a 不會(huì)隨著 b 的變化而變化呢?

因?yàn)閯傞_(kāi)始a 與 b 指向共用同一個(gè)值侣夷,但是后面 b 更換了新的值横朋,與原來(lái)的 1 斷開(kāi)了聯(lián)系,和 2 開(kāi)始了新的旅程百拓,a 依然指向 1琴锭,并不會(huì)因?yàn)?b 的走心受到影響晰甚。

進(jìn)擊的切圖仔

?? ?? ??

var a = {n: 1};
var b = a;
b.n = 3;

console.log(a.n) // 3

看圖我們可以看出,因?yàn)閯傞_(kāi)始a指向一個(gè)對(duì)象决帖,而對(duì)象和基本數(shù)據(jù)不太一樣厕九,基本數(shù)據(jù)是存儲(chǔ)在棧中,對(duì)象的話(huà)計(jì)算機(jī)會(huì)開(kāi)辟一個(gè)堆內(nèi)存來(lái)存儲(chǔ)這個(gè)對(duì)象地回,并且將這個(gè)堆的地址賦予對(duì)應(yīng)的變量扁远,但要對(duì)變量進(jìn)行操作的話(huà)通過(guò)地址對(duì)對(duì)應(yīng)堆中的對(duì)象進(jìn)行操作即可。


image

?? ?? ??

var a = {n: 1};
var b = a;
a.x = a = {n: 2};

console.log(a.x) // undefined
image

?? ?? ??

var a = {n: 1};

function fun(o){
    o.n = 2;
    o = {x: 555};
    o.y = 233;
    console.log(o) // {x: 555,y: 233}
}
fun(a)

console.log(a) // {n: 2}

不多解釋?zhuān)蹅冞€是直接上圖吧刻像!

image

其中我們還有一點(diǎn)需要找到畅买,就是函數(shù)執(zhí)行的時(shí)候發(fā)生了什么。

在我們函數(shù)執(zhí)行的時(shí)候细睡,在未執(zhí)行之前皮获,函數(shù)體內(nèi)的代碼是被視為字符串存放到堆內(nèi)存中的,所以在執(zhí)行會(huì)解析這些代碼字符串纹冤,并且執(zhí)行它洒宝;

在執(zhí)行的過(guò)程中

  1. 形成一個(gè)全新的私有上下文
    • 有存儲(chǔ)私有上下文中,聲明的私有變量空間 AO
    • 將上下文進(jìn)棧執(zhí)行
  2. 在執(zhí)行前需要做的事情
    • 初始化作用域鏈
    • 初始化 this 指向
    • 初始化 Arguments
    • 形參賦值
    • 變量提升
  3. 代碼執(zhí)行
  4. 一般情況下萌京,函數(shù)執(zhí)行完成后雁歌,為了優(yōu)化棧內(nèi)存,會(huì)將形成的私有上下文移出棧釋放掉知残,這就是我們一直說(shuō)的“GC瀏覽器垃圾回收機(jī)制”

以上就是筆者在這方面學(xué)習(xí)的總結(jié)靠瞎,你是否 Get 到了呢?

記得點(diǎn)擊關(guān)注和給波贊求妹,第一時(shí)間收到筆者專(zhuān)門(mén)為你推送的前端干貨乏盐,你還“下次一定”嗎,嚶嚶嚶~

進(jìn)擊的切圖仔
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末制恍,一起剝皮案震驚了整個(gè)濱河市父能,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌净神,老刑警劉巖何吝,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鹃唯,居然都是意外死亡爱榕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)坡慌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)黔酥,“玉大人,你說(shuō)我怎么就攤上這事」蛘撸” “怎么了棵帽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坑夯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)抡四,這世上最難降的妖魔是什么柜蜈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮指巡,結(jié)果婚禮上淑履,老公的妹妹穿的比我還像新娘。我一直安慰自己藻雪,他們只是感情好秘噪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著勉耀,像睡著了一般指煎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上便斥,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天至壤,我揣著相機(jī)與錄音,去河邊找鬼枢纠。 笑死像街,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晋渺。 我是一名探鬼主播镰绎,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼木西!你這毒婦竟也來(lái)了畴栖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤八千,失蹤者是張志新(化名)和其女友劉穎驶臊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體叼丑,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡关翎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸠信。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纵寝。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖耕赘,靈堂內(nèi)的尸體忽然破棺而出芬骄,到底是詐尸還是另有隱情,我是刑警寧澤嗅榕,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布室奏,位于F島的核電站火焰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胧沫。R本人自食惡果不足惜昌简,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绒怨。 院中可真熱鬧纯赎,春花似錦、人聲如沸南蹂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)六剥。三九已至晚顷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疗疟,已是汗流浹背音同。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秃嗜,地道東北人权均。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锅锨,于是被迫代替她去往敵國(guó)和親叽赊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353