您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關JavaScript抽象概念是怎樣的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
想必大伙兒看到本期的標題很有疑惑,為什么是簡而不單,單而不簡的執行上下文呢?我來先解釋一下,對于 javaScript 上一些抽象的概念,我們可以把它講的非常復雜,也可以把它講的極其簡單,更可以把它講的既復雜又簡單。
嗯~ 最近重新回顧了這些抽象的概念,發現有些概念之前并不能很好的融會貫通,所以把這些相對抽象難以理解的概念作為幾期文章來寫。
我個人覺得要想更好的理解抽象概念,不得不借助可視化的圖像減少作者和讀者之間的理解力和理解差錯。
1、執行上下文
1.1 JavaScript 引擎
說到執行上下文,不得不先扯扯 JavaScript 引擎,JavaScript 引擎是什么?考慮到這篇文章不專門寫 JavaScript 引擎,可以自己谷歌一下。說白了,JavaScript 引擎就是用來「解釋」、「編譯」和「執行」JavaScript 代碼的。
畢竟開發人員寫的 JS 代碼只能夠讓開發者認得出來,交給計算機,由于計算機只識別二進制,所以中間需要進行一系列的解釋和轉化才能看懂執行這些 JavaScript 代碼。
1.2 執行棧 (Execution stack)
小鹿注:保證 JavaScript 代碼的執行"順序"。
JavaScript 引擎既然可以執行 JS 的代碼,那么是按照什么順序執行的,又是怎么保證這些順序而不被所打亂的。先看一段簡單的代碼:
var foo2 = function () { console.log('foo2'); } var foo1 = function () { console.log('foo1'); foo2() console.log('foo3') } foo1(); // 輸出:“foo1 foo2 foo3”
通過上述代碼片段的執行,輸出的順序為'foo1 foo2 foo3'。
代碼執行,foo1()函數先執行,首先輸出'foo1',遇到 foo2() 函數的執行命令,將執行權交給 foo2, foo2 函數體執行,輸出'foo2'。foo2 執行完畢后,將執行權交回 foo1 函數,最后輸出'foo3'
我們可以找出上述代碼執行的規律,先執行的函數,會在最后退出,后執行的函數,先執行完畢。這個執行順序不就是“棧”的“先進后出”``“后進先出”的結構嘛。JavaScript 引擎將其這種執行結構稱為「執行棧」,用于保證 JavaScript 代碼的順序。
1.3 執行上下文(Exception Context)
小鹿注:將執行的代碼"模塊化" —— 執行上下文的分類。
什么是執行上下文?雖然我們在“執行上下文”詞義上很難直接理解,但是它具體代表的是什么,是很容易理解的,下面我把“執行上下文”的抽象概念進行具體化。
上述我們已經解釋了 JavaScript 引擎是使用執行棧來保證代碼的執行順序的,但是執行過程中需要涉及到一些變量的作用范圍界定(作用域)、閉包等復雜情況,我們需要 JavaScript 引擎引入一種機制來解決這些看起來復雜的問題,所以「執行上下文」的概念產生了。
但是,執行上下文是什么?這不得不讓我想起組件的模塊化開發,之前的一個網頁應用代碼從上到下一個文件寫下來幾千行代碼,難以閱讀、難以維護,所以有了后來的模塊化開發。每個模塊都有自己的功能,都有屬于自己的局部變量和樣式。
我們可以理解為 JavaScript 引擎為了更好的解釋和執行代碼,所以引入類似于像組件模塊的“執行上下文”的概念用于管理運行時代碼的復雜度。
2、執行上下文的分類
上述我們把抽象的“執行上下文”類似于“模塊”的具體概念便于理解。當然,執行上下文也就是所謂的“模塊”也有不同的分類,在這里具體只展開兩種,「全局執行上下文」和「局部執行上下文」。
2.1 全局執行上下文(Global Exception Context)
全局上下文這個“模塊”由兩部分組成,「全局對象」和「this」。
下圖是全局執行上下文的最基本形式。包含一個 window 對象,以及一個 this 變量,而這個 this 變量是指向 window 對象的,如最右圖的打印結果。
從這里我們看出,執行上下文可以理解為是一個在內存中的「對象和變量」集合的模塊(或者說是片段),這也是為什么我們可以把它看作類似“模塊”的原因(除此之外還有其他作用)
小鹿注:為了便于理解,定義是我自己總結的,如有欠缺歡迎指出~
2.2 局部執行上下文
局部執行上下文和全局執行上下文類似,但不完全相同,在函數局部執行上下文中,需要注意的有以下兩點:
函數傳入的參數會作為局部執行上下文的變量來存儲
局部上下文有一個 arguments 參數對象(參考)局部執行上下文內容會在下面的兩個階段中詳細講到。
3、執行上下文兩個階段
無論是全局執行上下文還是局部的執行上下文,都會經歷兩個階段,分別是「創建」和「執行」。
如下我們有一段代碼:
var name = "小鹿"; var age = 23; function getInfo(){ return { name: name, age: age }; }
3.1 創建階段(Creation)
創建階段要完成的事情,如下:
在堆內存中創建全局對象(global object)—— 瀏覽器環境是 windows,Node 環境是 Global
讓 this 變量指向這個全局對象
設置當前執行上下文中「變量和函數」的內存空間
將聲明的變量加入內存中(同時掛在到全局對象上),為變量賦值 undifined,函數存儲的是字符串形式
小鹿注:左 (1) 圖執行的代碼,左 (2) 圖創建階段完成后的執行上下文內存中狀態,右 (1) 創建階段全局對象的狀態。
JavaScript 引擎在執行代碼之前,先在堆內存中創建全局執行上下文,生成全局對象(global object),然后讓 this 變量指向這個變量。JavaScript 發現代碼中聲明的兩個變量 name 和 age,然后在全局執行上下文中申請內存空間,將變量存儲到該內存空間內,然后為該變量賦值 undefined,函數就以字符串的形式存儲在內存中。
小鹿注:在創建階段為變量聲明指定默認值(undefined)的過程稱為「變量提升」。
3.2 執行階段(Execution)
全局執行上下文創建完成之后,開始由創建狀態(Creation)變為執行狀態( Execution)。JavaScript 引擎開始逐行運行和執行代碼,并為在創建階段放入內存的變量賦予值。
小鹿注:左 (1) 圖執行的代碼,左 (2) 圖執行階段完成后的執行上下文內存中狀態,右 (1) 執行階段全局對象的狀態。
局部執行上下文和全局執行上下文的創建和執行過程是一模一樣的。但是全局執行上下文創建一次,而函數局部執行上下文是隨著函數的每次調用都要創建一個局部執行上下文。
還是上述例子,執行結果如下:
var name = "小鹿"; var age = 23; function getInfo(name){ console.log(name); return { name: name, age: age }; } getInfo(name);
函數局部上下文執行狀態如下:
小鹿注:由于函數中沒有定義新的變量,所以在這里沒有變量提升。
我們了解了什么是函數局部上下文,當函數局部上下文執行完畢之后,就會執行出棧操作,將執行權交給父級執行上下文(可能是局部執行上下文,也可能是全局執行上下文),上述 getInfo 函數執行完畢的狀態如下圖所示。
此時的函數執行完畢,局部執行上下文出棧銷毀,執行權交給全局執行上下文繼續執行其他代碼。
由于 JavaScript 是單線程的,一次只能執行一個任務,為了方便大伙兒理解,左(3)圖 是執行棧的調用情況。當然,我們也可以發現,左(2)圖是以嵌套的方式來模擬執行棧的操作,每一個嵌套選項都是堆棧中一個新的執行上下文。
執行上下文在 JavaScript 中是一個非常重要的概念,在接下幾期的進階文章中,作用域、作用域、閉包、this等概念,都會與本期文章內容掛鉤。
看完上述內容,你們對JavaScript抽象概念是怎樣的有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。