<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                # JavaScript的『預解釋』與『變量提升』 點擊關注本[公眾號](http://www.hmoore.net/book/dsh225/javascript_vue_css/edit#_118)獲取文檔最新更新,并可以領取配套于本指南的《**前端面試手冊**》以及**最標準的簡歷模板**. [TOC] ## 前言 JavaScript的作用域一直是JavaScript比較讓人頭痛的一部分,也是面試中幾乎必考的內容,因此,我們將從更深層次來講述js作用域。 ## 從一個實例開始 仔細閱讀以下JavaScript代碼,你覺得運行結果會是什么呢?是`1`還是`2`? ~~~ var a= 1; function f() { console.log(a); var a = 2; } f(); ~~~ 答案是undefined. 那么到底是什么原因導致了這個讓人意外的結果呢?這就要從JavaScript解釋階段說起。 ## JavaScript預解釋 我們可以大致把JavaScript在瀏覽器中運行的過程分為兩個階段`預解釋階段`(有人說準確的說法是應該是Parser,我們以預解釋方便理解)`執行階段`,在JavaScript引擎對JavaScript代碼進行執行之前,需要進行預先處理,然后再對處理后的代碼進行執行。 > 我們平時書寫的JavaScript代碼并不是JavaScript執行的代碼(V8引擎讀取一行執行一行這種理解是錯誤的),它需要預解釋后,再由引擎進行執行. 具體的解釋過程涉及到瀏覽器內核的技術不屬于前端領域,不過我們可以淺顯的理解一下V8在處理JavaScript的一般過程: 以上例中的`var a = 2;`為例,我們一般人的理解為**聲明了一個值為2的變量a**,但是在JavaScript引擎處理時卻分為了兩個步驟: > 1. 讀取`var a`后,在當前作用域中查找是否有相同聲明,如果沒有就在當前作用域集合中創建一個名為`a`的變量,否則忽略此聲明繼續進行解析. > 2. 接下來,V8引擎會處理`a = 2`的賦值操作,首先會詢問當前作用域中是否有名為`a`的變量,如果有進行賦值,否則繼續向上級作用域詢問. ## JavaScript執行環境 我們上面提到的所謂javascript預解釋正是創建函數的**執行環境**(又稱“執行上下文”),只有搞定了javascript的執行環境我們才能搞清楚一段代碼在執行過后為什么產生這樣的結果。 我們用一段偽代碼表示創立的**執行環境** ~~~ executionContextObj = { 'scopeChain': { /* 變量對象 + 所有父級執行上下文中的變量對象 */ }, 'variableObject': { /* 函數參數 / 參數, 內部變量以及函數聲明 */ }, 'this': {} } ~~~ 作用域鏈(scopeChain)包括下面提到的變量對象(variableObject)和所有父級執行上下文中的變量對象. 變量對象(variableObject)是與執行上下文相關的數據作用域,一個與上下文相關的特殊對象,其中存儲了在上下文中定義的變量和函數聲明: * 變量 * 函數聲明 * 函數的形參 在有了這些基板概念之后我們可以梳理一下js引擎創建執行的過程: * 創建階段 * 創建Scope chain * 創建variableObject * 設置this * 執行階段 * 變量的值、函數的引用 * 執行代碼 而變量對象的創建細節如下: * 根據函數的參數,創建并初始化arguments object * 掃描函數內部代碼,查找函數聲明(Function declaration) * 對于所有找到的函數聲明,將函數名和函數引用存入變量對象中 * 如果變量對象中已經有同名的函數,那么就進行覆蓋 * 掃描函數內部代碼,查找變量聲明(Variable declaration) * 對于所有找到的變量聲明,將變量名存入變量對象中,并初始化為"undefined" * 如果變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性 ## 變量提升 正是由于以上的處理,產生了大家熟知的JavaScript中的**變量提升**,具體以上代碼的執行過程如以下偽代碼所示: ~~~ // global context executionContextObj = { 'scopeChain': { ... }, 'variableObject': { a: undefined, f: pointer to function f() }, 'this': {...} } ... }//首先在全局執行環境中聲明了變量a以及函數f,此時a雖然被聲明,但是尚未賦值 x = 1; function f() { executionContextObj { 'scopeChain': { ... }, 'variableObject': { arguments: {}, a: undefined }, 'this': {...} } //內部詞法環境中聲明了變量a,此時a雖然被聲明,但是尚未賦值 console.log(a);//此時a需要被被打印出來,在作用域內尋找a變量賦值,于是被賦值undefined a = 2; } ~~~ 我們可以明顯看到,`a`變量在預解釋階段已經被賦值`undefined`,在執行階段js是自上而下單線執行,當`console.log(a)`執行之時,`a=2`還沒有被執行,`a`變量的值便是預處理階段被賦予的`undefined`, ## 函數聲明與函數表達式 我們看到,在編譯器處理階段,除了被`var`聲明的變量會有變量提升這一特性之外,函數也會產生這一特性,但是函數聲明與函數表達式兩種范式創建的函數卻表現出不同的結果. 我們先看一個實例,運行以下代碼 ~~~ f(); g(); //函數聲明 function f() { console.log('f'); } //函數表達式 var g = function() { console.log('g'); }; ~~~ `f`成功被打印出來,而`g函數`出現了類型錯誤,這是什么原因呢? ~~~ executionContextObj = { 'scopeChain': { ... }, 'variableObject': { f: pointer to function f(), g: undefined}, 'this': {...} } f(); g(); //函數聲明 function f() { console.log('f'); } //函數表達式 var g = function() { console.log('g'); }; ~~~ 我們看到,在預解釋階段函數聲明的`f`是被指向了正確的函數得以執行,而函數表達式`g`被賦予`undefined`,`undefined`無法被當作函數執行因此報錯`g is not a function`. ## 沖突處理 通常情況下我們不會將同一變量變量重復聲明,但是出現了類似情況后,編譯器會如何處理這些沖突呢? 1. 變量之間沖突 執行以下函數: ~~~ var a = 3; var a = 4; console.log(a); ~~~ 結果顯而易見,后聲明變量值覆蓋前者的值 2. 函數之間沖突 ~~~ f(); function f() { console.log('f'); } function f () { console.log('g'); }; ~~~ 結果同變量沖突,后者覆蓋前者. 3.函數與變量之間沖突 ~~~ console.log(f); function f() { console.log('f'); } var f ='g'; ~~~ 結果如下,函數聲明將覆蓋變量聲明 `[Function: f]` ## ES6中的let 在ES6中出現了兩個最新的聲明語法`let`與`const`,我們以`let`為例,進行測試看看與`var`的區別. ~~~ function f() { console.log(a); let a = 2; } f(); // ReferenceError: a is not defined ~~~ 這段代碼直接報錯顯示未定義,`let`與`const`擁有類似的特性,阻止了變量提升,當代碼執行到`console.log(a)`時,執行換將中`a`還從未被定義,因此產生了錯誤. * * * ## 公眾號 想要實時關注筆者最新的文章和最新的文檔更新請關注公眾號**程序員面試官**,后續的文章會優先在公眾號更新. **簡歷模板**:關注公眾號回復「模板」獲取 《**前端面試手冊**》:配套于本指南的突擊手冊,關注公眾號回復「fed」獲取 ![2019-08-12-03-18-41](https://xiaomuzhu-image.oss-cn-beijing.aliyuncs.com/d846f65d5025c4b6c4619662a0669503.png)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看