js——範圍和閉包

來源:互聯網
上載者:User

標籤:nbsp   元素   語言   全域   let   結構   聲明   單元   決定   

1. js是編譯語言,但是它不是提前編譯,編譯結果不能在分布式系統中移植。大部分情況下,js的編譯發生在代碼執行前的幾微秒(甚至更短)

2. 一般的編譯步驟
  • 分詞/詞法分析:把字串分解成詞法單元
  • 解析/文法分析:將詞法單元轉換成一個由元素組成的文法結構樹,抽象文法樹AST
  • 代碼產生:將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新引入的,將變數綁定在所在任意範圍{}中,且值是固定不可修改的
  • 運行時修改範圍  eval、with
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——範圍和閉包

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.