標籤:nbsp 元素 語言 全域 let 結構 聲明 單元 決定
1. js是編譯語言,但是它不是提前編譯,編譯結果不能在分布式系統中移植。大部分情況下,js的編譯發生在代碼執行前的幾微秒(甚至更短)
2. 一般的編譯步驟
- 解析/文法分析:將詞法單元轉換成一個由元素組成的文法結構樹,抽象文法樹AST
3. 三個工具
- 引擎:控制整個程式的編譯及執行過程
- 編譯器:負責文法分析及代碼產生等
- 範圍:收集並維護所有聲明的標識符的存取權限
4. var a = 2 的編譯過程
var a=2; |
--分解成--> |
詞法單元 var a = 2; => var、a、=、2 |
--解析成--> |
樹結構 AST |
--代碼產生--> 1. var a:詢問範圍是否有a。 如果有,則忽略。如果沒有,則在當前範圍添加一個聲明a 2. a = 2:當前範圍是否有a, 如果有,則賦值。如果沒有,則向上一層範圍尋找 |
5. 代碼產生中尋找判斷範圍是否存在某個變數的兩種尋找類型
|
LHS |
RHS |
直觀區別 |
變數在=左側 |
變數不在=左側 |
操作 |
對變數賦值 |
取變數的值 |
找不到? |
1. strict 模式 拋出ReferenceError異常 2. 非strict 模式 自動隱式建立一個全域變數 |
拋出ReferenceError異常 |
6. 範圍
定義在詞法階段的範圍。也就是在寫代碼時將變數和塊範圍寫在哪決定的。函數的範圍完全由聲明時的位置決定
-
- 全域範圍
- 函數範圍:每聲明一個函數就會建立一個範圍。在該範圍內聲明的變數或函數(標識符)都附屬於它,可在整個函數範圍內被使用。
- 塊範圍
var |
//變數綁定在所在的函數內(function c(){ //a是局部變數,b是全域變數 var a = b = 3;})()(function c(){ //a和b都是局部變數 var a = 1, b = 3;})() |
try/catch |
try{ //異常操作 //catch建立一個塊範圍,這裡的變數只能在catch中使用}catch(err){ //只有這裡可以訪問err} |
let |
ES6新引入的。將變數綁定在所在任意範圍{}中 在迴圈中for(let i = 1; i < 5; i++),i在每次迭代中會聲明,且每次迭代會用上一個迭代結束時的值來初始化 |
const |
ES6新引入的,將變數綁定在所在任意範圍{}中,且值是固定不可修改的 |
7. 範圍嵌套
在一個範圍A內建立一個新的範圍B,則B被嵌套在A中
B可以訪問A中的標識符。A不可以訪問B中的標識符
最外層的範圍是全域範圍
範圍層層嵌套形成範圍鏈,在訪問尋找一個標識符時從最內層開始向外尋找,一旦找到就停止,因此會出現外層的標識符被內層同名的所屏蔽
8. 閉包
在各個文章中對閉包進行瞭解釋,但是好像有很多說法。我理解得了的一個說法是:
當函數可以記住並訪問所在的詞法範圍時,就產生了閉包
函數A建立一個範圍A,在A中聲明一個函數B(建立了範圍B),把函數B作為結果返回,範圍B會記得自己的範圍鏈,利用B可以向上層範圍訪問
9. 迴圈和閉包(一個好像特別常見的例子)
for( var i = 1; i <= 5; i++){ setTimeout(function timer(){ console.log(i); }, i*1000);}
說明:
var i = 1:定義了一個全域變數
setTimeout():在i秒後執行timer函數。timer是回呼函數,會在for迴圈執行完成才會開始調用
結果:for執行完成後開始調用timer,以每秒一次的頻率輸出5次6
期待:每秒一次輸出1,2,3,4,5
結果解釋:
setTimeout時並沒有讓timer儲存i的副本
timer函數執行時,會去引用i的值,這時只有一個i=6
修改1——立即執行
for(var i = 1; i <=5; i++){ (function(){ setTimeout(function timer(){ console.log(i); }, i*1000);})();}
結果:以每秒一次的頻率輸出5次6
即使是立即執行,最後訪問的變數也是全域的i
修改2——立即執行+參數
for(var i = 1; i <=5; i++){ (function(j){ setTimeout(function timer(){ console.log(i); }, i*1000);})(i);}
結果:每秒一次輸出1,2,3,4,5
修改3——塊範圍迴圈變數
for(let i = 1; i <=5; i++){ setTimeout(function timer(){ console.log(i); }, i*1000);}
結果:每秒一次輸出1,2,3,4,5
修改4——在迴圈中添加一個變數var j
for(var i = 1; i <=5; i++){ var j = i; setTimeout(function timer(){ console.log(j); }, j*1000);}
結果:以每秒一次的頻率輸出5次5(j和i一樣為全域變數,j=5)
修改5——塊範圍變數
for(var i = 1; i <=5; i++){ let j = i; setTimeout(function timer(){ console.log(j); }, j*1000);}
結果:每秒一次輸出1,2,3,4,5
參考
1. 《你不知道的javascript》上卷
2. 還看了很多網上的說明,就不一一列舉了,因為沒記住具體哪些了
js——範圍和閉包