整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          手把手教會你JavaScript引擎如何執行JavaScript代碼

          avaScript 在運行過程中與其他語言有所不一樣,如果不理解 JavaScript 的詞法環境、執行上下文等內容,很容易會在開發過程中產生 Bug,比如this指向和預期不一致、某個變量不知道為什么被改了,等等。所以今天我們就來聊一聊 JavaScript 代碼的運行過程。

          大家都知道,JavaScript 代碼是需要在 JavaScript 引擎中運行的。我們在說到 JavaScript 運行的時候,常常會提到執行環境、詞法環境、作用域、執行上下文、閉包等內容。這些概念看起來都差不多,卻好像又不大容易區分清楚,它們分別都在描述什么呢?

          這些詞語都是與 JavaScript 引擎執行代碼的過程有關,為了搞清楚這些概念之間的區別,我們可以回顧下 JavaScript 代碼運行過程中的各個階段。


          JavaScript 代碼運行的各個階段

          JavaScript 是弱類型語言,在運行時才能確定變量類型。JavaScript 引擎在執行 JavaScript 代碼時,也會從上到下進行詞法分析、語法分析、語義分析等處理,并在代碼解析完成后生成 AST(抽象語法樹),最終根據 AST 生成 CPU 可以執行的機器碼并執行。

          這個過程,我們稱之為語法分析階段。除了語法分析階段,JavaScript 引擎在執行代碼時還會進行其他的處理。以 V8 引擎為例,在 V8 引擎中 JavaScript 代碼的運行過程主要分成三個階段。

          1. 語法分析階段。該階段會對代碼進行語法分析,檢查是否有語法錯誤(SyntaxError),如果發現語法錯誤,會在控制臺拋出異常并終止執行。
          1. 編譯階段。該階段會進行執行上下文(Execution Context)的創建,包括創建變量對象、建立作用域鏈、確定 this 的指向等。每進入一個不同的運行環境時,V8 引擎都會創建一個新的執行上下文。
          2. 執行階段。將編譯階段中創建的執行上下文壓入調用棧,并成為正在運行的執行上下文,代碼執行結束后,將其彈出調用棧。

          其中,語法分析階段屬于編譯器通用內容,就不再贅述。前面提到的執行環境、詞法環境、作用域、執行上下文等內容都是在編譯和執行階段中產生的概念。


          執行上下文的創建

          執行上下文的創建離不開 JavaScript 的運行環境,JavaScript 運行環境包括全局環境、函數環境和eval,其中全局環境和函數環境的創建過程如下:

          1. 第一次載入 JavaScript 代碼時,首先會創建一個全局環境。全局環境位于最外層,直到應用程序退出后(例如關閉瀏覽器和網頁)才會被銷毀。
          2. 每個函數都有自己的運行環境,當函數被調用時,則會進入該函數的運行環境。當該環境中的代碼被全部執行完畢后,該環境會被銷毀。不同的函數運行環境不一樣,即使是同一個函數,在被多次調用時也會創建多個不同的函數環境。


          在不同的運行環境中,變量和函數可訪問的其他數據范圍不同,環境的行為(比如創建和銷毀)也有所區別。而每進入一個不同的運行環境時,JavaScript 都會創建一個新的執行上下文,該過程包括:

          • 建立作用域鏈(Scope Chain);
          • 創建變量對象(Variable Object,簡稱 VO);
          • 確定 this 的指向。

          由于建立作用域鏈過程中會涉及變量對象的概念,因此我們先來看看變量對象的創建,再看建立作用域鏈和確定 this 的指向。


          創建變量對象

          變量對象(VO)

          每個執行上下文都會有一個關聯的變量對象,該對象上會保存這個上下文中定義的所有變量和函數。

          在瀏覽器中,全局環境的變量對象是window對象,因此所有的全局變量和函數都是作為window對象的屬性和方法創建的。相應的,在 Node 中全局環境的變量對象則是global對象。


          創建VO的過程

          創建變量對象將會創建arguments對象(僅函數環境下),同時會檢查當前上下文的函數聲明和變量聲明。

          • 對于變量聲明:此時會給變量分配內存,并將其初始化為undefined(該過程只進行定義聲明,執行階段才執行賦值語句)。
          • 對于函數聲明:此時會在內存里創建函數對象,并且直接初始化為該函數對象。


          變量聲明和函數聲明的處理過程,便是我們常說的變量提升和函數提升,其中函數聲明提升會優先于變量聲明提升。因為變量提升容易帶來變量在預期外被覆蓋掉的問題,同時還可能導致本應該被銷毀的變量沒有被銷毀等情況。因此 ES6 中引入了let和const關鍵字,從而使 JavaScript 也擁有了塊級作用域。


          作用域

          在各類編程語言中,作用域分為靜態作用域和動態作用域。JavaScript 采用的是詞法作用域(Lexical Scoping),也就是靜態作用域。詞法作用域中的變量,在編譯過程中會產生一個確定的作用域。


          詞法作用域中的變量,在編譯過程中會產生一個確定的作用域,這個作用域即當前的執行上下文,在 ES5 后我們使用詞法環境(Lexical Environment)替代作用域來描述該執行上下文。因此,詞法環境可理解為我們常說的作用域,同樣也指當前的執行上下文(注意,是當前的執行上下文)。


          在 JavaScript 中,詞法環境又分為詞法環境(Lexical Environment)和變量環境(Variable Environment)兩種,其中:

          • 變量環境用來記錄var/function等變量聲明;
          • 詞法環境是用來記錄let/const/class等變量聲明。

          也就是說,創建變量過程中會進行函數提升和變量提升,JavaScript 會通過詞法環境來記錄函數和變量聲明。通過使用兩個詞法環境(而不是一個)分別記錄不同的變量聲明內容,JavaScript 實現了支持塊級作用域的同時,不影響原有的變量聲明和函數聲明。

          這就是創建變量的過程,它屬于執行上下文創建中的一環。創建變量的過程會產生作用域,作用域也被稱為詞法環境。


          建立作用域鏈

          作用域鏈,就是將各個作用域通過某種方式連接在一起。作用域就是詞法環境,而詞法環境由兩個成員組成。

          1. 環境記錄(Environment Record):用于記錄自身詞法環境中的變量對象。
          2. 外部詞法環境引用(Outer Lexical Environment):記錄外層詞法環境的引用。

          通過外部詞法環境的引用,作用域可以層層拓展,建立起從里到外延伸的一條作用域鏈。當某個變量無法在自身詞法環境記錄中找到時,可以根據外部詞法環境引用向外層進行尋找,直到最外層的詞法環境中外部詞法環境引用為null,這便是作用域鏈的變量查詢。

          JavaScript 代碼運行過程分為定義期和執行期,前面提到的編譯階段則屬于定義期,代碼示例如下:

          function foo() { // 定義全局函數foo
              console.dir(bar);
              var a = 1;
              function bar() { // 在foo函數內部定義函數bar
                  a = 2;
           }
          }
          console.dir(foo);
          foo();

          前面我們說到,JavaScript 使用的是靜態作用域,因此函數的作用域在定義期已經決定了。在上面的例子中,全局函數foo創建了一個foo[[scope]]屬性,包含了全局[[scope]]

          foo[[scope]] = [globalContext];

          而當我們執行foo()時,也會分別進入foo函數的定義期和執行期。

          在foo函數的定義期時,函數bar的[[scope]]將會包含全局[[scope]]和foo的[[scope]]:

          bar[[scope]] = [fooContext, globalContext];

          運行上述代碼,我們可以在控制臺看到符合預期的輸出:

          可以看到:

          • foo的[[scope]]屬性包含了全局[[scope]]
          • bar的[[scope]]將會包含全局[[scope]]和foo的[[scope]]

          也就是說,JavaScript 會通過外部詞法環境引用來創建變量對象的一個作用域鏈,從而保證對執行環境有權訪問的變量和函數的有序訪問。除了創建作用域鏈之外,在這個過程中還會對創建的變量對象做一些處理。

          在編譯階段會進行變量對象(VO)的創建,該過程會進行函數聲明和變量聲明,這時候變量的值被初始化為 undefined。在代碼進入執行階段之后,JavaScript 會對變量進行賦值,此時變量對象會轉為活動對象(Active Object,簡稱 AO),轉換后的活動對象才可被訪問,這就是 VO -> AO 的過程,示例如下:

          function foo(a) {
              var b = 2; 
              function c() {}
              var d = function() {};
          }
          
          
          foo(1);

          在執行foo(1)時,首先進入定義期,此時:

          • 參數變量a的值為1
          • 變量b和d初始化為undefined
          • 函數c創建函數并初始化
          AO = {
           arguments: {
            0: 1,
            length: 1
           },
           a: 1,
           b: undefined,
           c: reference to function() c() {}
           d:undefined
          }

          前面我們也有提到,進入執行期之后,會執行賦值語句進行賦值,此時變量b和d會被賦值為 2 和函數表達式:

          AO = {
             arguments: {
              0: 1,
              length: 1
            },
            a: 1,
            b: 2,
            c: reference to function c(){},
            d: reference to FunctionExpression "d"
          }

          這就是 VO -> AO 過程。

          • 在定義期(編譯階段):該對象值仍為undefined,且處于不可訪問的狀態。
          • 進入執行期(執行階段):VO 被激活,其中變量屬性會進行賦值。

          實際上在執行的時候,除了 VO 被激活,活動對象還會添加函數執行時傳入的參數和arguments這個特殊對象,因此 AO 和 VO 的關系可以用以下關系來表達:

          AO = VO + function parameters + arguments

          現在,我們知道作用域鏈是在進入代碼的執行階段時,通過外部詞法環境引用來創建的。總結如下:

          • 在編譯階段,JavaScript 在創建執行上下文的時候會先創建變量對象(VO);
          • 在執行階段,變量對象(VO)被激活為活動對象( AO),函數內部的變量對象通過外部詞法環境的引用創建作用域鏈。

          通過作用域鏈,我們可以在函數內部可以直接讀取外部以及全局變量,但外部環境是無法訪問內部函數里的變量。示例如下:

          function foo() {
            var a = 1;
          }
          foo();
          console.log(a); // undefined

          我們在全局環境下無法訪問函數foo中的變量a,這是因為全局函數的作用域鏈里,不含有函數foo內的作用域。

          如果我們想要訪問內部函數的變量,可以通過函數foo中的函數bar返回變量a,并將函數bar返回,這樣我們在全局環境中也可以通過調用函數foo返回的函數bar,來訪問變量a:

          function foo() {
            var a = 1;
            function bar() {
              return a;
            }
            return bar;
          }
          var b = foo();
          console.log(b()); // 1

          當函數執行結束之后,執行期上下文將被銷毀,其中包括作用域鏈和激活對象。

          在上面的實例中;當b()執行時,foo函數上下文包括作用域都已經被銷毀了,但是foo作用域下的a依然可以被訪問到;這是因為bar函數引用了foo函數變量對象中的值,此時即使創建bar函數的foo函數執行上下文被銷毀了,但它的變量對象依然會保留在 JavaScript 內存中,bar函數依然可以通過bar函數的作用域鏈找到它,并進行訪問。這就是閉包;

          閉包使得我們可以從外部讀取局部變量,常見的用途包括:

          1. 用于從外部讀取其他函數內部變量的函數;
          2. 可以使用閉包來模擬私有方法;
          3. 讓這些變量的值始終保持在內存中。

          注意,在使用閉包的時候,需要及時清理不再使用到的變量,否則可能導致內存泄漏問題。


          確定 this 的指向

          在 JavaScript 中,this指向執行當前代碼對象的所有者,可簡單理解為this指向最后調用當前代碼的那個對象。

          根據 JavaScript 中函數的調用方式不同,this的指向分為以下情況。

          1. 在全局環境中,this指向全局對象(在瀏覽器中為window)
          2. 在函數內部,this的值取決于函數被調用的方式
          3. 函數作為對象的方法被調用,this指向調用這個方法的對象
          4. 函數用作構造函數時(使用new關鍵字),它的this被綁定到正在構造的新對象
          5. 在類的構造函數中,this是一個常規對象,類中所有非靜態的方法都會被添加到this的原型中
          6. 在箭頭函數中,this指向它被創建時的環境
          7. 使用apply、call、bind等方式調用:根據 API 不同,可切換函數執行的上下文環境,即this綁定的對象

          可以看到,this在不同的情況下會有不同的指向,在 ES6 箭頭函數還沒出現之前,為了能正確獲取某個運行環境下this對象,我們常常會使用以下代碼:

          var that = this;
          var self = this;

          這樣的代碼將變量分配給this,便于使用。但是降低了代碼可讀性,不推薦使用,通過正確使用箭頭函數,我們可以更好地管理作用域。


          總結

          今天我們了解了 JavaScript 代碼的運行過程,該過程分為語法分析階段、編譯階段、執行階段三個階段。

          在編譯階段,JavaScript會進行執行上下文的創建,在執行階段,變量對象(VO)會被激活為活動對象(AO),變量會進行賦值,此時活動對象才可被訪問。在執行結束之后,作用域鏈和活動對象均被銷毀,使用閉包可使活動對象依然被保留在內存中。這就是 JavaScript 代碼的運行過程。

          • 什么是 Python?
          • 什么是 JavaScript?
          • JavaScript 和 Python 的區別因素
          • Python 與 JavaScript:2022 年選擇哪種編程語言?

          Web 開發正在成為 IT 中最受歡迎的領域之一,毫無疑問,JavaScript 和 Python 都是 Web 開發人員中非常流行的編程語言。這兩種編程語言都有自己的優點,這使它們成為開發人員的熱門選擇。

          然而,Python 和 JavaScript 的比較一直是開發者社區的熱門討論。它經常會產生這樣的問題:python 比 javascript 難嗎?python比javascript好嗎?python和javascript有什么區別?javascript在Web開發中的用途是什么?等等等等。繼續閱讀本文,因為本文簡要回答了所有這些問題,但在討論這些編程語言在 Web 開發中的比較之前,了解什么是 Python 和 JavaScript 至關重要。

          什么是 Python?

          Python 是當今最好和最強大的編程語言之一。它用途廣泛,是大多數科學應用的必備工具。但是,它是一種通用編程語言,支持不同的編程范式。

          這種編程廣泛用于科學領域,包括數據科學、人工智能、計算機視覺 (CV)、DIP(數字圖像處理)、自然語言處理和 Web 開發。Python 的語法非常簡單。這就是為什么很多人認為 python 比 javaScript 好得多的原因。

          在 web 開發的背景下,有一個流行的問題:python 是用于前端還是后端?答案是python主要用于后端。這是我們可以將其功能和能力與 JavaScript 進行比較的地方。但是,需要注意的是,Python 不僅限于后端開發,還可以在一些附加工具的幫助下用于前端開發。

          什么是 JavaScript?

          雖然 Python 是后端開發的絕佳選擇,但 JavaScript 是首選,并且通常被稱為 Web 應用程序前端開發的編程語言。

          在這里了解后端和前端開發之間的區別很重要。Web 應用程序的后端是用戶無法訪問且用戶不直接與其交互的部分。它包含 Web 應用程序的所有業務邏輯和服務器端編程。

          另一方面,Web 應用程序的前端是最終用戶與之交互的部分。這是用戶可以訪問的應用程序的一部分。即使您正在與移動應用程序交互,您也很可能正在與 React.js(一個 JavaScript 框架)進行交互,它能夠開發能夠適應不同平臺的應用程序。

          JavaScript 和 Python 的區別因素

          現在,由于我們已經清楚地了解了 JavaScript 和 Python,我們可以更深入地挖掘它們的差異化因素。

          我們可以根據不同的理由來區分這兩種編程語言。python 和 JavaScript 都有各自的優缺點。因此,讓我們立即開始吧。

          人氣

          首先,重要的是要注意開發人員社區對這兩種編程語言的看法。確定開發人員選擇的最佳矩陣是評估這些編程語言的流行程度。

          根據Stack Overflow 的 2020 年開發者調查,JavaScript 在最常用的編程語言列表中名列前茅。根據調查的詳細信息,幾乎 69.7% 的開發社區使用 JavaScript。相比之下,Python 的普及率在 41.7% 的開發者中。

          JavaScript 流行的一個原因是它的工作可以廣泛使用。此外,它不僅可用于 Web 開發,而且 JavaScript 還可用于移動應用程序開發,這使其成為開發社區中的熱門選擇。

          表現

          性能是開發網站時要牢記的最重要因素之一。如果應用程序不夠高效,它幾乎是無用的。這就是新的編程語言試圖灌輸盡可能多的效率和優化的原因。

          然而,這并不容易,而是一項非常棘手的任務。這也取決于用于開發的編程語言。因為每種編程語言都有自己的內置規則和方法來提高效率。

          Web 應用程序的速度取決于 Web 應用程序中代碼的執行速度。構建 JavaScript 的目的是在 Web 瀏覽器上快速運行。這就是 Facebook、Google、Amazon 等應用程序使用 JavaScript 的原因。

          如果將 Python Web 應用程序與 Node.js Web 應用程序進行比較,Node.js 肯定會勝過 Python。因為據專家介紹,Python 在單個流中處理請求,但 JavaScript 可以利用其先進的多線程功能一次處理多個請求。

          應用程序可擴展性

          對于一個人來說,Web 應用程序的可擴展性就像氧氣一樣是生存所必需的。考慮一個應用程序,該應用程序響應速度快、交互性強,并提供非常需要的服務。但是,它無法同時處理大量請求。這將導致網絡流量下降,因為如果用戶打開一個網站并且它沒有按需要響應或經常崩潰,但用戶最終也會尋找其他一些快速的替代方案。

          因此,應用程序的可擴展性非常重要。因為可伸縮性允許應用程序一次處理大量的流量和請求。為了支持可擴展性,多線程是一個非常可行的解決方案。但是 Python 不支持多線程,因為它適用于單線程系統。相反,它使用全局解釋器鎖來一次處理多個請求。

          相反,Node.js 純粹是為了支持異步編程而開發的。因此,JavaScript 在 Web 應用程序可擴展性方面表現出色。但是,Python 也可以通過上述方式實現可擴展性。

          內存管理

          內存管理是另一個需要考慮的重要方面。如果一種編程語言不能有效地管理內存,將直接影響網站的優化。

          在 Python 中,所有變量和數據結構都收集在一個堆中。Python 也有它的內部內存管理器來確保對這個堆的管理。

          但是,JavaScript 使用一種稱為 GC(垃圾收集器)的自動內存管理工具。但是,GC 的管理工作在一個近似算法上,因為它無法知道某個內存位置是否無用。

          范圍

          編程語言的范圍定義了它的多功能性。如果我們談論 Python 的應用程序或用例,我們就會知道 Python 用于后端的 Web 開發。它在軟件開發、計算和腳本系統中也非常有用。它在數據科學和機器學習中非常流行。

          相比之下,JavaScript 也支持所有這些類型的軟件開發。JavaScript 相對于 python 的最大優勢在于,它不僅是一種前端語言,而且在開發應用程序的服務器端邏輯時也能工作得非常好。此外,JavaScrip 還用于開發移動應用程序,目前 Python 不支持。React.js 是流行的移動應用程序開發框架之一。


          Python 與 JavaScript:2022 年選擇哪種編程語言?

          根據上面進行的討論,很明顯這兩種語言都有自己的優缺點。幾乎不可能質疑 Python 是否會取代 JavaScript,反之亦然。

          對于 JavaScript 和 Python,開發人員的選擇將基于所考慮平臺的要求。例如,當語言的設計至關重要時,Python 是您的選擇。如果需要移動開發和高度響應的網站,那么 JavaScript 是您的選擇。

          更全面地說,如果所需的應用程序必須具有可擴展性和高效性,那么 JavaScript 是正確的選擇。而如果網站后臺非常復雜,可讀性好,又要求編程簡單,可以選擇Python。

          關于是選擇Python還是前端,我的建議是這樣的,在學歷這塊,目前Python和前端這塊招聘學歷最低需求都是大專,所以這塊問題不大,其次你最想要知道的就是選擇Python還是前端,下面我從市場發展前景以及薪資待遇這兩方面具體的分析一下:

          先說下Python,Python這個編程語言在近兩年比較火,之所以比較火,在很大程度上面要歸根于培訓機構,很多培訓機構借助于人工智能的熱潮從而進行炒作,以此招生盈利。

          現在我認為Python主要的三個方向,web開發,網絡爬蟲,人工智能。

          如果你選擇做web開發,我更建議你選擇Javaweb,Javaweb的發展以及提升空間更大。

          其次是網絡爬蟲,網絡爬蟲我現在不建議你學,因為現在這個方向現在這方面的崗位比較少。

          其次就是人工智能,這個方向就更不建議你考慮了,因為門檻比較高,最低學歷需求都是碩士以上,因為涉及到很多算法方面的東西。而且現在Python這個語言的話,發展得不是很完善,相當于你現在學了出來,只能在一線城市找到合適的崗位工作,二線三線城市基本上,沒有這方面的崗位。

          如果你選擇學習Python的話,這三個方向你最終只能選擇Python web開發,剛學完技術出來,你可以考慮去一線城市發展,提升一下自己的閱歷,技術,經驗等等,但是萬事都需要從長遠考慮,目前所在的城市以后是否會具備這方面的崗位。

          我們來看下目前Python的薪資待遇水平:

          目前在北京等這樣的一線城市Python崗位的薪資待遇水平在20k左右(有一定的水分),當然這是具備一定開發經驗的,一般剛學完Python技術出來的,只要能找到相關的工作,薪資待遇大約都在5-7k左右。其次我在職友集上面搜索一下一些二線城市Python崗位,基本上沒有相關的崗位招聘。基本上這個就是目前Python的一個市場行情。

          下面我們來說下前端,web前端這個語言在國內流行了很多年的時間,可以說現在無論你是在一線城市,還是二線三線城市,你學了前端,都可以找到合適的崗位工作。

          雖說現在前端的基數大,但是真正懂前端技術,會寫代碼的前端非常的少。

          從現在的大企業來講,他是不要那種單純的只會去做機械性勞動的前端。這種競爭就比較少,所以在前端這個領域里面來看,機會非常的多。所以你們一定要成為真正的前端,而不是就成為只會切個圖,做機械性勞動的前端。

          我們來看下目前前端崗位的薪資待遇水平:

          目前在北京等這樣的一線城市,前端方面的薪資待遇在18k左右,當然這也是具備一定開發經驗的前端,屬于比較正常的薪資待遇水平。

          剛學完前端技術出來的初級前端一般薪資待遇在5-7k左右。

          這里給你提供一個web前端的系統學習路線:

          web前端基礎部分的HTML,CSS都比較簡單,比較難的部分就是后面JavaScript部分。

          總的來說,非要我建議的話我更建議你學習web前端。因為前端這個工作更加穩定,不管是二線三線都比較好找工作,而且相比起Python,前端更容易掌握。

          以上就是關于PYthon和前端HTML5之間的區別,大家可以根據自身情況考慮是該選擇哪個方面。


          主站蜘蛛池模板: 亚洲狠狠久久综合一区77777 | 国产亚洲综合精品一区二区三区 | 人妻无码一区二区三区AV| 韩国福利一区二区三区高清视频 | 国产一区二区三区乱码在线观看 | 日本一区二区三区精品视频| 文中字幕一区二区三区视频播放 | 一区二区三区免费电影| 99久久精品日本一区二区免费| 91麻豆精品国产自产在线观看一区 | 国产精品一区二区三区久久 | 日本高清一区二区三区| 国产麻豆精品一区二区三区v视界| 成人免费视频一区| 日韩欧美一区二区三区免费观看| 久久久久人妻精品一区二区三区| 国产亚洲一区区二区在线 | 国产精品高清一区二区三区| 国产激情精品一区二区三区| 一区二区免费电影| 全国精品一区二区在线观看| 国产精品视频免费一区二区三区 | 亚洲熟女www一区二区三区 | 国产激情精品一区二区三区| 国产激情精品一区二区三区| 成人区人妻精品一区二区不卡视频| 日韩电影在线观看第一区| 精彩视频一区二区| 久久精品一区二区三区中文字幕| 亚洲视频在线一区二区| 国模无码一区二区三区不卡| 久久久99精品一区二区| 国产在线不卡一区二区三区 | 毛片一区二区三区无码| 无码aⅴ精品一区二区三区| 国产精品资源一区二区| 日韩精品一区二区三区影院| 一区二区三区免费电影| 国产成人无码一区二区在线播放| 久久国产精品一区免费下载| 精品国产一区二区三区无码|