理解JavaScript中的做用域

   什麼是變量,什麼是做用域?

    

     變量:簡單來講就是在特定時間內保存特定值的一個名字而已,因爲不存在定義某個變量必需要保存某種數據類型值的規則,因此變量的值及其數據類型能夠在腳本生命週期內任意改變,變量可能包含兩種不一樣的數據類型得值:基本類型值和引用類型值。基本數據類型包括:Undedind、Null、Boolean、Number、String和Symbol(es6新增),同時Boolean、Number、String和Symbol也叫基本包裝類型。引用類型:簡單來講,除了全部基本類型剩下的就是引用類型了。可能不嚴謹,先這樣對付看吧。前端

      做用域:咱們已經知道了變量的做用其實就是存儲值,那麼咱們要怎麼樣才能對值進行訪問或者修改?換句話說,這些變量住在哪裏呢,更重要的是咱們(程序)要如何找到他們?像莎士比亞說得那樣:To be, or not to be: that is the question!因此咱們須要一套設計良好的規則來存儲變量,而且能夠方便的找到這些變量,這套規則就叫做用域!es6

   

  編譯原理

    在瞭解做用於規則前,咱們先要知道編譯原理,即咱們的代碼寫下以後是怎麼工做的在強類型語言中,程序中要執行一段源代碼首先要通過編譯,編譯過程有三個步驟:編程

      一、分詞/詞法分析。這個過程將字符組成的字符串分解成有意義的代碼塊,這些代碼塊被稱爲詞法單元,例如 var a = 2;這段代碼一般會被分解成爲下面這些詞法單元:var、a、=、二、;。空格是否被看成詞法單元取決於空格對於這門語言是否有意義。數組

      二、解析/語法分析。這個過程是將詞法單元數組轉換成一個由元素逐級嵌套所組成的表明程序語法結構的樹,叫「抽象語法樹」(Abstract Syntax Tree,AST)。簡單來講,對於編程語言下的源代碼,經過構建語法樹的形式將源代碼中的代碼映射到樹中的每個節點上。編程語言

      三、代碼生成。將AST(抽象語法樹)轉換爲可執行的代碼這一過程叫作代碼生成。簡單來講就是將 var a  = 2;的AST轉化成機器能認識的指令,建立一個變量a,而後他的值2儲存在a中。函數

   

     比起那些編譯過程只有三個步驟的編譯器,JavaScript引擎要複雜的多,在語法分析和代碼生成階段有特定的步驟來對性能進行優化,包括對冗餘元素進行優化等,具體有哪些步驟,我也不知道,由於編譯原理這段內容是摘抄《《你不知道的JavaScirpt 上卷》》書中的原話,哈哈哈,是否是很吃精!!!JS的編譯大部分發生在代碼執行前的,而且編譯完成立刻執行。性能

   

  理解做用域

    請看var a = 2;這段代碼,正常人會認爲這是一句生明,由於語句中沒有‘,’或者’。‘等語句分隔符號,可是在JS引擎中認爲這裏有兩個聲明:變量生命和賦值生明。首先是詞法分析過程,將var a = 2;分解成詞法單元,而後語法分析生成抽象語法樹,最後執行代碼。具體一點說,var a = 2;會分解成var a 和a = 2;當編譯器遇到var  a,時,編譯器會詢問做用域是否已經存在一個該名稱的變量存於同一個做用域中,若是是則會忽略該生明(es6中新增的let和const狀況有點不太同樣),若是沒有存在,則在當前做用域中生命一個新的變量,命名爲a。接下來編譯器會爲引擎生成運行時所須要的代碼,這段代碼來處理a=2這個賦值操做。引擎運行時首先會在當前做用域中查找是否有一個叫作a的變量,若是有就賦值,若是沒有就繼續向上一級做用域查找,若是實在是找不到,找到了做用域頂層全局做用域也找不到,那麼就會拋出一個錯誤。因此變量賦值操做總會執行兩個步驟,若是變量未聲明則在當前做用域中生名一個變量(嚴格模式下若是給未聲明的變量賦值會報錯),而後運行時引擎會在做用域中查找改變量,若是找到了就進行賦值。優化

  做用域嵌套

    做用域是怎麼造成的呢?簡單來說就是執行環境所決定的,每個執行環境都有一個與之關聯的變量對象,環境內的全部變量和函數都保存在這個對象中,在全局環境下執行保存在全局的變量對象中叫全局做用域,在函數內的變量保存在函數內部的變量對象構成局部做用域。當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈,做用域鏈的前端始終都是當前代碼所在環境的變量對象中。編譯器在查找變量時就是沿着做用域鏈一級一級地搜索標識的過程,直到全局執行環境也就是全局做用域爲止。當一個做用域嵌套在另外一個做用域中,就發生了做用域嵌套,所以在當前做用域中沒法找到某個變量時,引擎就會沿着做用域鏈在外層嵌套的做用域中查找,知道找到該變量或者在做用域鏈頂端(全局做用域)爲止。以下:spa

function foo(a){
    console.log(a+b);
}
var b = 2;
foo(2); // 4

這段代碼中,引擎首先會在foo函數中問foo的做用域,你見過b嗎?foo做用域:沒見過,滾犢子。而後引擎沿着做用域鏈往上爬,爬到了全局做用域中,而後問:全局做用域大哥你見過b嗎?全局做用域:嗯 我見過,給你吧。設計

嵌套做用域規則很簡單,就是在當前做用域開始,一級一級地向上查找。直到抵達最外層全局做用域,查找過程就會中止!

以前說過,變量在賦值操做前老是會先查找而後再賦值,若是在非嚴格模式下,查找不到所要賦值的變量,全局做用域中會隱式的建立一個具備該名稱的變量並返回給引擎。以下:

function foo(a){
    console.log(a+b);
    b = a;
}
foo(2); // 4

 

 很久以前寫的,好像我是在抄書,照本宣科嗎?哈哈

相關文章
相關標籤/搜索