1、js沒有塊級做用域函數
在強類型語言中都有塊級做用域,例如,有以下C#代碼spa
for(int i = 0; i < 10; i++) { //do something } Console.WriteLine(i);
這樣的代碼是沒法經過編譯的,由於循環變量i是一個局部變量,它的做用域範圍只在for循環的大括號{}以內,出了這個大括號就不是i的做用域了,因此Console.WriteLine(i); 這條語句就不能編譯經過了。3d
但若是這樣相似的代碼是js寫的就另當別論了。好比:code
for(var i = 0; i < 10; i++){ //do something; } console.log(i);//輸出10
這個代碼是沒有問題的,運行結果是10 。這說明了js沒有傳統意義上的塊級做用域。對象
但這不表明js沒有局部做用域。js的做用域只有全局做用域和函數做用域,js也只有函數能產生局部做用域。blog
1、全局做用域ip
只要是在一對<script>標籤以內直接聲明的對象,它的做用域就是全局的。作用域
若是一個對象是在function內部聲明的,那麼它的做用域就是局部的,函數內部的做用域。it
好比:io
<script> var a = 123; console.log(a); //輸出123 function f1(){ console.log(a); } f1();//輸出123 </script>
此例中,a就是直接聲明在了一對<script>標籤內的,不是聲明在某一個函數以內的,那麼它是個全局做用域,因此不論是在f1函數內部,仍是在外部,均可以訪問到變量a
2、函數做用域
把上邊的例子稍微改改
<script> var a = 123; console.log(a); //輸出123 function f1(){ var num = 10; console.log(a); } f1();//輸出123 console.log(num); // 報錯,沒法訪問到num </script>
這個代碼會報錯,由於num是在f1函數內部聲明的,它的做用域僅限於f1函數聲明的內部,也就是f1那個大括號{}內部,因此在函數聲明的外部訪問num確定是不能夠的。
咱們可能會看到一個詞,說js中的做用域是:詞法做用域。那麼怎麼理解這個所謂的「詞法做用域」呢?
看這個例子
<script> var a = 123; console.log(a); //輸出123 function f1(){ console.log(a); } function f2(){ var a = 456; f1(a); } f2();//輸出123 </script>
這個例子就很值得思考了。
1.有一個全局做用域的變量a
2.f1函數調用了a
3.f2函數先聲明瞭一個局部變量a,而且與全局變量同名
4.f2函數聲明瞭這個同名的局部變量a以後,又調用了f1函數
這個程序執行完以後的結果是 : 123 , 而不是456。緣由是js中不存在塊級做用域,只存在函數做用域。
而函數做用域又被稱之爲詞法做用域,說白了就是:你的函數時在哪兒聲明的,那它就屬於哪兒,也就意味着它只能訪問到這個區間的成員。
在這個例子中,f1函數是聲明在全局做用域下的,那麼它就屬於全局做用域,也就意味着它(函數體內部)只能訪問到全局做用域下聲明的對象。顯然,當前全局做用域下的a是等於123的,而不是f2內部聲明的那個等於456的a,因此,不論是在什麼時候調用f1這個函數,它訪問到的a必然是那個值等於123的全局變量a。
那究竟該如何清晰的描述出對象的做用域範圍?這就要使用一個叫作「做用域鏈」的東西了。
3、做用域鏈
看這個例子就明白什麼是做用域鏈了。
<script> var num = 1; function f1(){ var num = 2; function f2(){ var num = 3; function f3(){ console.log(num); } f3(); } f2(); } f1(); //輸出 3 // f2();//報錯, // f3();//報錯 </script>
這個例子就是函數嵌套聲明,而且每一個函數內部還都又聲明瞭一個跟全局變量num同名的局部變量。相信經過剛纔的解釋,你們已經能清晰的得出程序執行結果了。
簡單說明一下:
1.在全局做用域下,不能直接調用f2和f3函數,由於它們都是在f1內部嵌套聲明的,它們都是局部做用域聲明的對象,因此不能在全局做用域中訪問。
2.在局部做用域中,若是出現於更高級的做用域同名的對象,那麼優先訪問本做用域範圍以內的對象
3.若是在本做用域以內找不到,那麼就往「上一級」做用域中去找,知道全局做用域,若是尚未就報錯。
那麼這裏所謂的「上一級」做用域,可使用做用域鏈清晰的描繪出來。這個例子的做用域鏈咱們能夠用圖畫出來。
首先這裏的全局做用域咱們稱之爲 0 級做用域,f1函數時在全局做用域下聲明的,因此f1本身造成一個小的做用域,稱之爲 1 級。以此類推。
當咱們在外部調用f2和f3是不可能的。由於從圖上咱們清晰的看到,在0級做用域的橫線上根本沒有聲明一個叫f2或者f3的對象。
當咱們在全局(0級)做用域下,調用f1函數,而後逐級的調用到f3的時候,f3內部有一條console.log(num)。此時,程序會沿着咱們畫的線,如今f3的做用域,也就是3級做用下找有沒有聲明一個叫num的對象,若是有就直接拿來打印,若是沒有,則沿着這個做用域鏈往上遞推,首先推到離他最近的上一級做用域,也就是f2的做用域,它是2級做用域,這裏聲明瞭一個叫作num的變量,而且賦了初值爲3,因而,這條打印語句,就馬上把這個=3的num拿來使用,而再也不理會更高級的做用域了。由於它已經找到了一個能夠訪問到的num。
咱們把這個圖描繪的做用域之間的關係,就形象的稱之爲做用域鏈。
在做用域鏈中搜索對象,是隻能逐級往上查找的,而不能往低級的做用域中查找。