js做用域與做用域鏈

一直對Js的做用域有點迷糊,今天偶然讀到JavaScript權威指南,立馬被吸引住了,寫的真不錯。我看的是第六版本,至關的厚,大概1000多頁,Js博大精深,要熟悉精通須要大毅力大功夫。javascript

一:函數做用域html

   先看一小段代碼:java

[javascript]  view plain  copy
  1. var scope="global";  
  2. function t(){  
  3.     console.log(scope);  
  4.     var scope="local"  
  5.     console.log(scope);  
  6. }  
  7. t();  

 

(PS: console.log()是firebug提供的調試工具,很好用,有興趣的童鞋能夠用下,比瀏覽器+alert好用多了)瀏覽器

第一句輸出的是: "undefined",而不是 "global"app

第二講輸出的是:"local"函數

  你可能會認爲第一句會輸出:"global",由於代碼還沒執行var scope="local",因此確定會輸出「global"。工具

  我說這想法徹底沒錯,只不過用錯了對象。咱們首先要區分Javascript的函數做用域與咱們熟知的C/C++等的塊級做用域。oop

  在C/C++中,花括號內中的每一段代碼都具備各自的做用域,並且變量在聲明它們的代碼段以外是不可見的。而Javascript壓根沒有塊級做用域,而是函數做用域.this

所謂函數做用域就是說:-》變量在聲明它們的函數體以及這個函數體嵌套的任意函數體內都是有定義的。spa

因此根據函數做用域的意思,能夠將上述代碼重寫以下:

 

[javascript]  view plain  copy
  1. var scope="global";  
  2. function t(){  
  3.     var scope;  
  4.     console.log(scope);  
  5.     scope="local"  
  6.     console.log(scope);  
  7. }  
  8. t();  

    咱們能夠看到,因爲函數做用域的特性,局部變量在整個函數體始終是由定義的,咱們能夠將變量聲明」提早「到函數體頂部,同時變量初始化還在原來位置。

爲何說Js沒有塊級做用域呢,有如下代碼爲證:

 

[javascript]  view plain  copy
  1. var name="global";  
  2. if(true){  
  3.     var name="local";  
  4.     console.log(name)  
  5. }  
  6. console.log(name);  

都輸出是「local",若是有塊級做用域,明顯if語句將建立局部變量name,並不會修改全局name,但是沒有這樣,因此Js沒有塊級做用域。

 

 

如今很好理解爲何會得出那樣的結果了。scope聲明覆蓋了全局的scope,可是尚未賦值,因此輸出:」undefined「。

因此下面的代碼也就很好理解了。

 

[javascript]  view plain  copy
  1. function t(flag){  
  2.     if(flag){  
  3.         var s="ifscope";  
  4.         for(var i=0;i<2;i++)   
  5.             ;  
  6.     }  
  7.     console.log(i);  
  8.     console.log(s);  
  9. }  
  10. t(true);  

輸出:2  」ifscope"


 

二:變量做用域

仍是首先看一段代碼:

 

[javascript]  view plain  copy
  1. function t(flag){  
  2.     if(flag){  
  3.         s="ifscope";  
  4.         for(var i=0;i<2;i++)   
  5.             ;  
  6.     }  
  7.     console.log(i);  
  8. }  
  9. t(true);  
  10. console.log(s);  


就是上面的翻版,知識將聲明s中的var去掉。

 

程序會報錯仍是輸出「ifscope"呢?

讓我揭開謎底吧,會輸出:」ifscope"

這主要是Js中沒有用var聲明的變量都是全局變量,並且是頂層對象的屬性。

因此你用console.log(window.s)也是會輸出「ifconfig"

 

當使用var聲明一個變量時,建立的這個屬性是不可配置的,也就是說沒法經過delete運算符刪除

var name=1    ->不可刪除

sex=」girl「         ->可刪除

this.age=22    ->可刪除

 

三:做用域鏈

先來看一段代碼:

 

[javascript]  view plain  copy
  1. name="lwy";  
  2. function t(){  
  3.     var name="tlwy";  
  4.     function s(){  
  5.         var name="slwy";  
  6.         console.log(name);  
  7.     }  
  8.     function ss(){  
  9.         console.log(name);  
  10.     }  
  11.     s();  
  12.     ss();  
  13. }  
  14. t();  


當執行s時,將建立函數s的執行環境(調用對象),並將該對象置於鏈表開頭,而後將函數t的調用對象連接在以後,最後是全局對象。而後從鏈表開頭尋找變量name,很明顯

 

name是"slwy"。

但執行ss()時,做用域鏈是: ss()->t()->window,因此name是」tlwy"

下面看一個很容易犯錯的例子:

 

[html]  view plain  copy
  1. <html>  
  2. <head>  
  3. <script type="text/javascript">  
  4. function buttonInit(){  
  5.     for(var i=1;i<4;i++){  
  6.         var b=document.getElementById("button"+i);  
  7.         b.addEventListener("click",function(){ alert("Button"+i);},false);  
  8.     }  
  9. }  
  10. window.onload=buttonInit;  
  11. </script>  
  12. </head>  
  13. <body>  
  14. <button id="button1">Button1</button>  
  15. <button id="button2">Button2</button>  
  16. <button id="button3">Button3</button>  
  17. </body>  
  18. </html>  

當文檔加載完畢,給幾個按鈕註冊點擊事件,當咱們點擊按鈕時,會彈出什麼提示框呢?

 

很容易犯錯,對是的,三個按鈕都是彈出:"Button4",你答對了嗎?

當註冊事件結束後,i的值爲4,當點擊按鈕時,事件函數即function(){ alert("Button"+i);}這個匿名函數中沒有i,根據做用域鏈,因此到buttonInit函數中找,此時i的值爲4,

因此彈出」button4「。

 

四:with語句

說到做用域鏈,不得不說with語句。with語句主要用來臨時擴展做用域鏈,將語句中的對象添加到做用域的頭部。

看下面代碼

[javascript]  view plain  copy
  1. person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};  
  2. with(person.wife){  
  3.     console.log(name);  
  4. }  

with語句將person.wife添加到當前做用域鏈的頭部,因此輸出的就是:「lwy".

 

with語句結束後,做用域鏈恢復正常。

相關文章
相關標籤/搜索