摘要:所以覺得把這個執(zhí)行的詳細過程整理一下,幫助更好的理解。類似的語法報錯的如下圖所示三預編譯階段代碼塊通過語法分析階段之后,語法都正確的下回進入預編譯階段。另開出新文章詳細分析,主要介紹執(zhí)行階段中的同步任務執(zhí)行和異步任務執(zhí)行機制事件循環(huán)。
一、概述
js是一種非常靈活的語言,理解js引擎的執(zhí)行過程對于我們學習js是非常有必要的。看了很多這方便文章,大多數(shù)是講的是事件循環(huán)(event loop)或者變量提升的等,并沒有全面分析其中的過程。所以覺得把這個js執(zhí)行的詳細過程整理一下,幫助更好的理解js。
1.1基礎概念js是單線程語言。
在瀏覽器中一個頁面永遠只有一個線程在執(zhí)行js腳本代碼
js是單線程怨言,但是代碼解析是非常迅速的,不會發(fā)生解析阻塞。
js是異步執(zhí)行的,通過實踐循環(huán)(event loop)方式實現(xiàn)的
暫時我們不考慮事件循環(huán)(event loop),我們先來看這樣一段代碼,來確定我們是否理解js引擎的執(zhí)行過程
console.log(person) console.log(personFun) var person = "saucxs"; console.log(person) function personFun() { console.log(person) var person = "songEagle"; console.log(person) } personFun() console.log(person)
可以自己直接使用瀏覽器看出輸出結果
首先我們來分析一下上面的代碼,雖然很多開發(fā)人員基本上都能答出來,但是還是要啰嗦一下。
全面分析js引擎的執(zhí)行過程,分為三個階段
1、語法分析
2、預編譯階段
3、執(zhí)行階段
說明:瀏覽器先按照js的順序加載
三、預編譯階段js代碼塊通過語法分析階段之后,語法都正確的下回進入預編譯階段。
在分析預編譯階段之前,我們先來了解一下js的運行環(huán)境,運行環(huán)境主要由三種:
1、全局環(huán)境(js代碼加載完畢后,進入到預編譯也就是進入到全局環(huán)境)
2、函數(shù)環(huán)境(函數(shù)調(diào)用的時候,進入到該函數(shù)環(huán)境,不同的函數(shù),函數(shù)環(huán)境不同)
3、eval環(huán)境(不建議使用,存在安全、性能問題)
每進入到一個不同的運行環(huán)境都會創(chuàng)建 一個相應的執(zhí)行上下文(execution context),那么在一段js程序中一般都會創(chuàng)建多個執(zhí)行上下文,js引擎會以棧的數(shù)據(jù)結構對這些執(zhí)行進行處理,形成函數(shù)調(diào)用棧(call stack),棧底永遠是全局執(zhí)行上下文(global execution context),棧頂則永遠時當前的執(zhí)行上下文。
3.1函數(shù)調(diào)用棧什么是函數(shù)調(diào)用棧?
函數(shù)調(diào)用棧就是使用棧存取的方式進行管理運行環(huán)境,特點是先進后出,后進后出
我們來分析一下簡答的js代碼來理解函數(shù)調(diào)用棧:
function bar() { var B_context = "bar saucxs"; function foo() { var f_context = "foo saucxs"; } foo() } bar()
上面代碼塊通過語法分析后,進入預編譯階段,如圖所示
1、首先進入到全局環(huán)境,創(chuàng)建全局執(zhí)行上下文(global Execution Context ),推入到stack中;
2、調(diào)用bar函數(shù),進入bar函數(shù)運行環(huán)境,創(chuàng)建bar函數(shù)執(zhí)行上下文(bar Execution Context),推入stack棧中;
3、在bar函數(shù)內(nèi)部調(diào)用foo函數(shù),則再進入到foo函數(shù)運行環(huán)境中,創(chuàng)建foo函數(shù)執(zhí)行上下文(foo Execution Context),如上圖,由于foo函數(shù)內(nèi)部沒有再調(diào)用其他函數(shù),那么則開始出棧;
5、foo函數(shù)執(zhí)行完畢之后,棧頂foo函數(shù)執(zhí)行上下文(foo Execution Context)首先出棧;
6、bar函數(shù)執(zhí)行完畢,bar函數(shù)執(zhí)行上下文(bar Execution Context)出棧;
7、全局上下文(global Execution Cntext)在瀏覽器或者該標簽關閉的時候出棧。
說明:不同的運行環(huán)境執(zhí)行都會進入到代碼預編譯和執(zhí)行兩個階段,語法分析則在代碼塊加載完畢時統(tǒng)一檢查語法。
3.2創(chuàng)建執(zhí)行上下文執(zhí)行上下文可以理解成當前的執(zhí)行環(huán)境,與該運行環(huán)境相對應。創(chuàng)建執(zhí)行上下文的過程中,主要是做了下面三件事,如圖所示:
1、創(chuàng)建變量對象(variable object)
2、創(chuàng)建作用域鏈(scope chain)
3、確定this的指向
3.2.1創(chuàng)建變量對象創(chuàng)建變量對象主要是經(jīng)過以下過程,如圖所示:
1、創(chuàng)建arguments對象,檢查當前上下文的參數(shù),建立該對象的屬性與屬性值,僅在函數(shù)環(huán)境(非箭頭函數(shù))中進行的,全局環(huán)境沒有此過程。
2、檢查當前上下文的函數(shù)聲明,按照代碼順序查找,將找到的函數(shù)提前聲明,如果當前上下文的變量對象沒有該函數(shù)名屬性,則在該變量對象以函數(shù)名建立一個屬性,屬性值則指向該函數(shù)所在堆內(nèi)存地址引用,如果存在,則會被新的引用覆蓋掉。
3、檢查當前上下文的變量聲明,愛去哪找代碼順序查找,將找到的變量提前聲明,如果當前上下文的變量對象沒有變量名屬性,則在該變量對象以變量名建立一個屬性,屬性值為undefined;如果存在,則忽略該變量聲明。
說明:在全局環(huán)境中,window對象就是全局執(zhí)行上下文的變量對象,所有的變量和函數(shù)都是window對象的屬性方法。
所以函數(shù)聲明提前和變量聲明提升是在創(chuàng)建變量對象中進行的,且函數(shù)聲明優(yōu)先級高于變量聲明。
下面我們再來分析這個簡單代碼
function fun(m,n){ var saucxs = 1; function execution(){ console.log(saucxs) } } fun(2,3)
這里我們在全局環(huán)境中調(diào)用fun函數(shù),創(chuàng)建fun的執(zhí)行上下文,這里暫時不說作用域鏈以及this指向的問題。
funEC = { //變量對象 VO: { //arguments對象 arguments: { m: undefined, n: undefined, length: 2 }, //execution函數(shù) execution:, //num變量 saucxs: undefined }, //作用域鏈 scopeChain:[], //this指向 this: window }
1、funEC表示fun函數(shù)的執(zhí)行上下文(fun Execution Context 簡寫為funEC);
2、funEC的變量對象中arguments屬性,上面這樣寫只是為了理解,在瀏覽器中展示以類數(shù)組的方式展示的
3、
說明:創(chuàng)建變量對象發(fā)生在預編譯階段,還沒有進入到執(zhí)行階段,該變量對象都不能訪問的,因為此時的變量對象中的變量屬性尚未賦值,值仍為undefined,只有在進行執(zhí)行階段,變量中的變量屬性才進行賦值后,變量對象(Variable Object)轉(zhuǎn)為活動對象(Active Object)后,才能進行訪問,這個過程就是VO->AO過程。
3.2.2創(chuàng)建作用域鏈作用域鏈由當前執(zhí)行環(huán)境的變量對象(未進入到執(zhí)行階段前)與上層環(huán)境的一系列活動對象組成,保證了當前執(zhí)行還款對符合訪問權限的變量和函數(shù)有序訪問。
理解清楚作用域鏈可以幫助我們理解js很多問題包括閉包問題等,下面我們結合一個例子來理解一下作用域鏈。
var num = 30; function test() { var a = 10; function innerTest() { var b = 20; return a + b } innerTest() } test()
在上面例子中,當執(zhí)行到調(diào)用innerTest函數(shù),進入到innerTest函數(shù)環(huán)境。全局執(zhí)行上下文和test函數(shù)執(zhí)行上下文已進入到執(zhí)行階段,innerTest函數(shù)執(zhí)行上下文在預編譯階段創(chuàng)建變量對象,所以他們的活動對象和變量對象分別是AO(global),AO(test)和VO(innerTest),而innerTest的作用域鏈由當前執(zhí)行環(huán)境的變量對象(未進入到執(zhí)行階段前)與上層環(huán)境的一系列活動對象組成,如下:
innerTestEC = { //變量對象 VO: {b: undefined}, //作用域鏈 scopeChain: [VO(innerTest), AO(test), AO(global)], //this指向 this: window }
我們這里可以直接使用數(shù)組表示作用域鏈,作用域鏈的活動對象或者變量對象可以直接理解成作用域。
1、作用域鏈的第一項永遠是當前作用域(當前上下文的變量對象或者活動對象);
2、最后一項永遠是全局作用域(全局上下文的活動對象);
3、作用域鏈保證了變量和函數(shù)的有序訪問,查找方式是沿著作用域鏈從左至右查找變量或者函數(shù),找到則會停止找,找不到則一直查找全局作用域,再找不到就會排除錯誤。
3.2.3閉包什么是閉包?思考一下
看一下簡單的例子
function foo() { var num = 20; function bar() { var result = num + 20; return result } bar() } foo()
因為對于閉包的有很多的不同理解,包括我看一些書籍(js高級程序設計),我這直接以瀏覽器解析,以瀏覽器的閉包為準來分析閉包,如圖
如圖所示,谷歌瀏覽器理解的閉包是foo,那么按照瀏覽器的標準是如何定義的閉包,自己總結為三點:
1、在函數(shù)內(nèi)部定義新函數(shù)
2、新函數(shù)訪問外層函數(shù)的局部變量,即訪問外層函數(shù)環(huán)境的活動對象屬性
3、新函數(shù)執(zhí)行,創(chuàng)建新函數(shù)的執(zhí)行上下文,外層函數(shù)即為閉包
3.2.4確定this指向1、在全局環(huán)境下,全局執(zhí)行的上下文中變量對象的this屬性指向為window;
2、在函數(shù)環(huán)境下的this指向比較靈活,需要根據(jù)執(zhí)行環(huán)境和執(zhí)行方法確定,列舉典型例子來分析
四、總結由于涉及到的內(nèi)容過多,下一次將第三階段(執(zhí)行階段)多帶帶分離出來。另開出新文章詳細分析,主要介紹js執(zhí)行階段中的同步任務執(zhí)行和異步任務執(zhí)行機制(事件循環(huán)(Event Loop))。
五、參考書籍你不知道的javascript(上卷)
同步songEagle(首發(fā)):(http://www.chengxinsong.cn/po...
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/100838.html
摘要:所以覺得把這個執(zhí)行的詳細過程整理一下,幫助更好的理解。類似的語法報錯的如下圖所示三預編譯階段代碼塊通過語法分析階段之后,語法都正確的下回進入預編譯階段。另開出新文章詳細分析,主要介紹執(zhí)行階段中的同步任務執(zhí)行和異步任務執(zhí)行機制事件循環(huán)。 一、概述 js是一種非常靈活的語言,理解js引擎的執(zhí)行過程對于我們學習js是非常有必要的。看了很多這方便文章,大多數(shù)是講的是事件循環(huán)(event loo...
摘要:如果對語法分析和預編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負責解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務的整個過程。 一、概述 js引擎執(zhí)行過程主要分為三個階段,分別是語法分析,預編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進行語法...
摘要:如果對語法分析和預編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負責解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務的整個過程。一、概述 js引擎執(zhí)行過程主要分為三個階段,分別是語法分析,預編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進行語法檢驗,語...
摘要:遮蔽效應作用域查找會在找到第一個匹配的標識符時停止,不會繼續(xù)往上層作用域查找,這就會產(chǎn)生遮蔽效應。會發(fā)現(xiàn)每一次輸出的都是為啥勒所有的回調(diào)函數(shù)回在循環(huán)結束后才會執(zhí)行事件循環(huán)。 三劍客 編譯,顧名思義,就是源代碼執(zhí)行前會經(jīng)歷的過程,分三個步驟, 分詞/詞法分析,將我們寫的代碼字符串分解成多個詞法單元 解析/語法分析,將詞法單元集合生成抽象語法樹(AST) 代碼生成,抽象語法樹(AST)轉(zhuǎn)...
摘要:作用域鏈用于表明上下文的執(zhí)行順序。當前上下文執(zhí)行完畢則出棧,執(zhí)行下一個上下文。 從一個簡單的例子出發(fā) 先從一個簡單的例子出發(fā)(先不涉及異步),看看自己是否大致了解瀏覽器的執(zhí)行機制: console.log(a); var a=1; function foo(a){ console.log(a); var a=2; console.log(a); } foo(a)...
閱讀 1967·2021-10-25 09:48
閱讀 2797·2021-09-22 14:59
閱讀 1759·2019-08-29 16:52
閱讀 861·2019-08-29 16:07
閱讀 2304·2019-08-29 12:38
閱讀 1756·2019-08-26 13:23
閱讀 878·2019-08-26 11:49
閱讀 3270·2019-08-26 10:56