標籤:程式設計語言 程式 編譯器 手機 如何
範圍作為一個最基礎的功能存在於各種程式設計語言中,它使得我們的編程更加靈活有趣。其基礎功能就是儲存變數中的值,然後可以對值進行訪問和修改。
可能我們都知道範圍的一些概念,以及其一些擴充的一些內容閉包等,但是相對於這些可能我們去瞭解這些變數到底是存到了哪裡,而我們的程式是如何訪問到他們的會更加有趣。
var a = 1;
首先我們要瞭解到在我們進行聲明變數並進行賦值的時候到底誰參與了我們的整個流程。
1,引擎:它參與了整個JS程式的編譯和執行。
2,編譯器:它負責了文法分析和代碼的產生。
3,範圍:它負責手機並維護所有的標識符也就(變數)組成的一系列查詢,並對此實施了一套十分嚴格的規則,確定當前執行的代碼對這些標識符的存取權限。
當引擎看到var a =1;的時候它和我們所想的是不一樣的,我們想這是一個聲明,他所認為的其實是這裡有倆完全不同的聲明,一個由編譯器在編譯的時候處理,另一個則由引擎在運行時候處理。那我們看看他們到底是怎麼工作的。
編譯器會先講var a = 1;這段程式分解成詞法單元,然後將詞法單元解析成一個樹結構,但是當編譯器開始進行代碼產生的時候,他對這段程式的處理方式會有所不同。
在我們理解的範圍內,編譯器是這樣工作的:為一個變數分配記憶體,將其命名為a,然後講值1儲存進這個變數,但事實是不同的。
1,遇到 var a 的時候,編譯器會先詢問範圍是否已經有一個該名稱的變數存在於同一個範圍的集合中。如果是,編譯器就會忽略該聲明並繼續進行編譯,不然的話他會要求範圍在當前範圍的集合中聲明一個變數命名為a。
2,然後編譯器開始為引擎產生運行所需的代碼,這些代碼會被用來處理 a = 1 這個賦值的操作。然後引擎運行,它會先詢問範圍,當前範圍集合中是否存在了一個叫a的變數,如果是引擎就會使用這個變數,如果否,引擎會繼續在當前範圍的上級範圍尋找該變數。最後只要找到了a,引擎就會把1賦值給它,如果沒有找到,引擎就會拋出一個異常。
所以說:在新變數的賦值中存在兩個操作,第一個是編譯器聲明變數,第二個就是引擎在範圍中尋找該變數,並對其賦值。
下面看一段簡單的代碼
function demo(a){ console.log(a) }demo(2);
在繼續說之前,我們先看看LHS和RHS,顧名思義,一個左一個右。這是引擎對變數所用的兩個查詢,L和R代表的是一個賦值的左側和右側(大部分情況下),也就是說當變數出現在賦值的左側時進行LHS查詢,出現在右側時進行RHS查詢。但是更確切的說法其實是RHS查詢只是簡單的尋找某個變數的值,而LHS查詢則是找到變數的容器本身,從而對其進行賦值操作。那麼根據這個說法,我們可以發現RHS其實代表的應該是“非左側”。我們看一個簡單代碼。
var a = 1;console.log(a);
在上面的代碼中,var a =1;對a的引用是一個LHS引用,而console.log(a)的a其實就是一個RHS引用。再看一個例子:
function demo(a){ console.log(a); }demo(1);
上面的代碼中其實包含了RHS和LHS引用。我們理解下demo(1);的意思,它其實意味著RHS引用demo這個值,(...)意味著它需要被執行,而在執行的過程中,有一個隱式的LRS引用 a = 1; 這個查詢發生在參數傳遞的過程中。其中的console.log(a)也是個RHS引用。
同時我們需要知道,不成功的RHS會導致拋出一個異常,而不成功的LHS引用會導致自動隱式的建立一個全域變數(非strict 模式),或者拋出異常(strict 模式)。
範圍的嵌套
當一個塊或者函數嵌套在另一個塊或者函數內時,就發生了範圍的嵌套。因此,當在當前範圍中無法找到該變數時,引擎就會在外層嵌套的範圍中繼續尋找(若一直沒有找到會到達全域範圍),直到找到該變數。
650) this.width=650;" src="http://images2015.cnblogs.com/blog/964875/201610/964875-20161017104824857-1188301210.png" style="margin:0px;padding:0px;border:none;" />
一層一層往外形成的結構就是我們常說的範圍鏈,最內層代表當前執行環境的範圍,最外層代表全域範圍。LHS和RHS都會在當前範圍進行尋找,如果沒有找到,就會向外,以此類推,如果到達全域範圍都沒有找到,那無論如何這個尋找過程都會停止。
理解Javascript--範圍和賦值操作