(轉載)Javascript 進階 做用域 做用域鏈

載請標明出處:http://blog.csdn.net/lmj623565791/article/details/25076713javascript

一直以爲Js很強大,因爲長期不寫js代碼,最近恰好溫故溫故。java

一、Javascript沒有代碼塊做用域的概念,局部做用域是針對函數來講的。閉包

 

[javascript]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. function fun()  
  2. {  
  3.     for( var i = 0 ; i < 10 ; i++)  
  4.     {}  
  5.     //若是在Java中i此時應當屬於未聲明的變量,可是Js中i的做用域依然存在  
  6.     console.log(i);//10  
  7.   
  8.     if(true)  
  9.     {  
  10.         var b = "helloworld";  
  11.     }  
  12.     console.log(b);//helloworld  
  13. }  
  14. fun();  

 

 

二、若是不使用var聲明的變量,默認爲全局變量函數

 

[javascript]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. function fun02()  
  2. {  
  3.     a = "helloworld";  
  4.     var b = "welcome";  
  5. }  
  6. fun02();  
  7. console.log(a); //     helloworld  
  8. console.log(b); //   b is not defined  


三、Js中的做用域鏈this

 

先看個簡單的例子:只有一個函數對象,函數對象和其它對象同樣,擁有能夠經過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內部屬性。其中一個內部屬性是[[Scope]],由ECMA-262標準第三版定義,該內部屬性包含了函數被建立的做用域中對象的集合,這個集合被稱爲函數的做用域鏈,它決定了哪些數據能被函數訪問。spa

 

[javascript]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. var a = "hello";  
  2.   
  3.       function fun04()  
  4.       {  
  5.            a = "world";  
  6.             var b ="welcome";  
  7.       }  

做用域鏈的圖:.net

 


注:圖中省略了,Global Scope中的window,document等,每一個函數對象中的arguments,this等均未畫出。code

 

[javascript]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. function fun03()  
  2.       {  
  3.           var a = 10;  
  4.           return function(){  
  5.               a*= 2 ;  
  6.               return a ;  
  7.           };  
  8.       }  
  9.   
  10.       var f = fun03();  
  11.       f();  
  12.       var x = f();  
  13.      console.log(x);  //40  
  14.   
  15.       var g = fun03();  
  16.       var y = g();  
  17.       console.log(y); //20  


觀察上面代碼,存在fun03,f,g三個函數對象。對象

 

下面是做用域鏈的圖:blog

注:每一個函數對象一個做用域鏈,這裏直接畫在了一塊兒;對於變量的查找,先從鏈的0開始找。

函數對象 f 在代碼中執行了2 次,因此a*2*2 = 40 ; 函數對象 g 在代碼中執行了1次, 因此 a *2 = 20 ; 

四、閉包

上面的例子能夠看到,在fun03執行完成後,a的實例並無被銷燬,這就是閉包。我的對閉包的理解是:函數執行完成後,函數中的變量沒有被銷燬,被它返回的子函數所引用。

下面以一個特別經典的例子,同時使用做用域鏈解析:

 

[javascript]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. window.onload = function()  
  2.         {  
  3.             var elements = document.getElementsByTagName("li");  
  4.             for(var i = 0; i < elements.length ; i ++)  
  5.             {  
  6.                 elements[i].onclick = function()  
  7.                 {  
  8.                     alert(i);  
  9.                 }  
  10.             }  
  11.   
  12.         }  


相信上面的代碼確定你們都寫過,本意是點擊每一個li,打印出它們的索引,但是事實上打印出的都是elements.length。這是爲何呢?

 

看下上面的簡易的做用域鏈(省略了不少部分,主要是理解),此時每一個onclick函數的i,指向的都是 onload 中的i 此時的 i = element.length.

下面看解決方案:

 

[javascript]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. window.onload = function ()  
  2.        {  
  3.            var elements = document.getElementsByTagName("li");  
  4.            for (var i = 0; i < elements.length; i++)  
  5.            {  
  6.                (function (n)  
  7.                {  
  8.                    elements[n].onclick = function ()  
  9.                    {  
  10.                        alert(n);  
  11.                    }  
  12.                })(i);  
  13.            }  
  14.   
  15.        }  


在onclick函數的外層,包了一層當即執行的函數,因此此時的n指向的 n 是當即執行的,全部都是1~elements.length 。

----------------------------------------

以上原子 摘抄的  (文章開頭的連接)

----------------------------------------

其實,關於上面循環中,i獲取到的始終是  elements.length的問題,只須要 將 i 的值定義一個變量 存放一下,也能夠解決。

相關文章
相關標籤/搜索