如何理解JavaScript中的範圍

來源:互聯網
上載者:User

如何理解JavaScript中的範圍

什麼是變數,什麼是範圍?

變數:簡單來說就是在特定時間內儲存特定值的一個名字而已,由於不存在定義某個變數必須要儲存某種資料類型值的規則,所以變數的值及其資料類型可以在指令碼生命週期內任意改變,變數可能包含兩種不同的資料類型得值:基本類型值和參考型別值。基礎資料型別 (Elementary Data Type)包括:Undedind、Null、Boolean、Number、String和Symbol(es6新增),同時Boolean、Number、String和Symbol也叫基本封裝類型。參考型別:簡單來說,除了所有基本類型剩下的就是參考型別了。可能不嚴謹,先這樣對付看吧。

範圍:我們已經知道了變數的作用其實就是儲存值,那麼我們要怎麼樣才能對值進行訪問或者修改?換句話說,這些變數住在哪裡呢,更重要的是我們(程式)要如何找到他們?像莎士比亞說得那樣:To be, or not to be: that is the question!所以我們需要一套設計良好的規則來儲存變數,並且可以方便的找到這些變數,這套規則就叫範圍!

編譯原理

    在瞭解作用於規則前,我們先要知道編譯原理,即我們的代碼寫下之後是怎麼工作的。在強型別語言中,程式中要執行一段原始碼首先要經過編譯,編譯過程有三個步驟:

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

      2、解析/文法分析。這個過程是將詞法單元數群組轉換成一個由元素逐級嵌套所組成的代表程式文法結構的樹,叫“抽象文法樹”(Abstract Syntax Tree,AST)。簡單來說,對於程式設計語言下的原始碼,通過構建文法樹的形式將原始碼中的代碼映射到樹中的每一個節點上。

      3、代碼產生。將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的變數,如果有就賦值,如果沒有就繼續向上一級範圍尋找,如果實在是找不到,找到了範圍頂層全域範圍也找不到,那麼就會拋出一個錯誤。所以變數賦值操作總會執行兩個步驟,如果變數未聲明則在當前範圍中生名一個變數(strict 模式下如果給未聲明的變數賦值會報錯),然後運行時引擎會在範圍中尋找改變數,如果找到了就進行賦值。

  範圍嵌套

    範圍是怎麼形成的呢?簡單來講就是執行環境所決定的,每一個執行環境都有一個與之關聯的變數對象,環境內的所有變數和函數都儲存在這個對象中,在全域環境下執行儲存在全域的變數對象中叫全域範圍,在函數內的變數儲存在函數內部的變數對象構成局部範圍。當代碼在一個環境中執行時,會建立變數對象的一個範圍鏈,範圍鏈的前端始終都是當前代碼所在環境的變數對象中。編譯器在尋找變數時就是沿著範圍鏈一級一級地搜尋標識的過程,直到全域執行環境也就是全域範圍為止。當一個範圍嵌套在另一個範圍中,就發生了範圍嵌套,因此在當前範圍中無法找到某個變數時,引擎就會沿著範圍鏈在外層嵌套的範圍中尋找,知道找到該變數或者在範圍鏈頂端(全域範圍)為止。如下:

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

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

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

之前說過,變數在賦值操作前總是會先尋找然後再賦值,如果在非strict 模式下,尋找不到所要賦值的變數,全域範圍中會隱式的建立一個具有該名稱的變數並返回給引擎。如下:

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

OK。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.