咱們知道JavaScript是一門編譯語言,並且它不是提早編譯的。JS的編譯發生在代碼執行前的很是短的時間內。
程序中的源代碼在執行前會通過分詞、解析和代碼生成三個階段,咱們把這三個步驟統稱爲編譯。react
在react中咱們常常會用到state來表示組件的狀態。狀態這個詞對於編程語言來講,我的感受是比較重要的,若沒有狀態這個詞,程序雖然也能執行一些簡單的任務,但會限制程序的靈活性。
在編程語言中,咱們使用變量來表示狀態。幾乎任何一種編程語言都有存儲、訪問和修改變量值的功能,正是這種存儲和訪問變量值得能力將狀態帶給了程序。
既然,有了變量,那麼咱們就須要考慮把變量存在哪裏以及如何在須要它們的時候可以找到它們?這時候咱們就須要設計一套良好的規則來存儲和訪問變量。做用域其實就是這套存儲和訪問變量的規則。
做用域有三種:編程
在之後總結閉包的時候再來詳細解釋一下這三種做用域。畢竟,閉包和做用域和預編譯過程都是密不可分的。
爲了更好地理解做用域,咱們還要了解一下引擎和編譯器。bash
當咱們看到var a = 2是咱們就會把這句話當作一句聲明,但引擎會認爲這裏有兩個聲明,一個由編譯器在編譯階段處理,一個由引擎在運行時處理。
下面咱們將var a = 2進行分解。編譯器首先會將這行代碼分解成詞法單元,而後將這些詞法單元解析成一個樹結構。最後生成可執行的代碼。閉包
事實上,編譯器會作以下工做。編程語言
1.遇到var a,編譯器會詢問當前做用域內是否已經存在a變量,若是存在,則忽略該變量聲明,繼續往下編譯。若是沒有改變量,則在當前做用域內聲明一個名爲a的變量。
2.接下來編譯器會爲引擎生成運行時所需的代碼來進行a = 2的賦值操做。在執行階段,引擎會在當前做用域下查找a變量,若是找到a變量,引擎便會使用a變量,若是沒有找到,引擎會繼續查找該變量。函數
這兩種方法就是引擎查找變量的方法。
LHS和RHS的含義是「賦值操做的左側或右側」並不意味着就是「=賦值操做符的左側或右側」。賦值操做還有其餘幾種形式,所以在概念上最好將其理解爲「賦值操做的目標是誰(LHS)」以及「誰是賦值操做的源頭(RHS)」ui
特性:spa
var a = 2;
a();
複製代碼
function foo(a) {
var b = a;
return a + b;
}
var c = foo(2);
複製代碼
LHS有3處:c = ..; a = 2(隱式變量分配),b = ..
RHS有4處:foo(2..、 = a; return 中的a和b設計
當一個塊或函數嵌套在另外一個塊或函數中時,就發生了做用域的嵌套。所以,在當前做用域中沒法找到某個變量時,引擎就會在外層嵌套的做用域中繼續查找,直到找到改變量或抵達最外層的做用域(全局做用域)爲止。
做用域鏈查找其實和預編譯是緊密相連的。3d
預編譯四步曲:
function fn(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
var b = function () {};
console.log(b);
function d() {}
}
fn(1);
複製代碼
1.建立AO
AO { }
2. 找形參和變量聲明,將變量和形參名做爲AO屬性名,值爲undefined
AO { a: undefined, b: undefined, }
3. 將實參值和形參統一
AO { a: 1, b: undefined, }
4. 在函數體裏面找函數聲明,值賦予函數體
AO { a: function a(){}, b: undefined,d: function d() {}}
預編譯完成後,引擎便要開始執行代碼了。
function fn(a) {
console.log(a);//此時的AO中爲function a(){}
var a = 123;// var a 在預編譯時已經執行,不用再執行,此刻執行a = 123, AO中的a由函數變成a: 123
console.log(a);// 123
function a() {};// 預編譯已經執行,不用再執行
console.log(a);// AO中a仍是123
var b = function () {};// var b已經執行,執行 b = function(){},AO中b的值由undefined變成匿名函數
console.log(b);// function() {}
function d() {}:// 已經在編譯階段執行了,不在執行
}
fn(1);
複製代碼
因此以上代碼的結果爲function a(){},123,123,function(){}
再來練習一個
function a(age) {
console.log(age);
var age = 20;
console.log(age);
function age() {
}
console.log(age);
}
a(18);
複製代碼
預編譯執行完成後,引擎開始執行代碼
function a(age) {
console.log(age);// function age(){}
var age = 20;// var age在預編譯是已經執行完,引擎執行age = 20, AO{age:function age(){}}變成AO{age:20}
console.log(age);// 20
function age() {
}// 預編譯時已經執行完
console.log(age);// AO中的age仍是20
}
a(18);
複製代碼
因此以上結果爲function age(){},20,20