初識JavaScript閉包

一個問題引起的思考

在我學習javascript的事件時,有一個小任務是使用JS來實現 li 列表項在鼠標懸浮時會有背景陰影的動態效果,很天然想到用for 來爲每一個列表項添加onmouseover 和 onmouseout事件來改變和恢復 li 的類名。javascript

以下:html

 1 <script type="text/javascript">
 2   var Lis = document.getElementsByTagName("li");
 3 
 4   function addevent() {
 5     for (var i = 0; i < Lis.length; i++) {
 6       Lis[i].onmouseover = function() {
 7         console.log(i);
 8         Lis[i].className = "lihover";
 9       }
10       Lis[i].onmouseout = function() {
11         Lis[i].className = "";
12       }
13     }
14   }
15   addevent();
16   // console.log(i);
17 </script>

 

看起來頗有道理的代碼會什麼不能正常工做?java

先看一下爲何編程

在第8行和11行,經過改變 li 元素的classname來實現鼠標懸浮的動態效果改變,根據我之前學習的語言(C和JAVA)這明顯是不對的,怎麼能在函數內使用外面的局部變量呢,但是瀏覽器爲何沒有報錯,我在7行加了一句console.log(i);看一下瀏覽器

瀏覽器輸出了3 並報錯:closure.html:44 Uncaught TypeError: Cannot set property 'className' of undefined閉包

當這兩個事件發生時,函數會執行,函數會訪問Lis 和 i 這兩個變量,可是注意循環和事件函數不是同時執行的,事件函數發生時i的 已是3了, 而Lis裏並無Lis[3]這個元素。函數式編程

但是這兩個函數爲何能訪問外面的局部變量呢,答案是 閉包 (closure)函數

閉包初識

 閉包就是在建立函數時爲其保存一份建立時的外部環境,因此這兩個事件函數可以訪問到i這個變量,雖然訪問的是循環執行完後的i的值。這也已經很神奇了對吧,源自函數式編程的魔法。那麼怎麼讓其訪問的 i 是函數自身被建立時的 i 呢,我再建立一個函數專門用來返回這個事件函數,以下:學習

 1   function makeevents(i) {
 2     console.log(i);
 3     return function() {
 4       Lis[i].className = "lihover";
 5     }
 6   }
 7 
 8   function addevent() {
 9     for (var i = 0; i < Lis.length; i++) {
10       Lis[i].onmouseover = makeevents(i);
11       Lis[i].onmouseout = function() {
12         this.className = "";
13       }
14     }
15   }
16   addevent();

我只是在mouseover事件用了這個機制,在mouseout使用了this關鍵詞這個等最後在討論。this

在瀏覽器裏運行一下發現效果已經實現了,而且在頁面加載完後控制檯就打印出了0 1 2 

makeevent函數的做用正是建立閉包,在每一次循環建立一個,這樣Lis[i]引用的就是正確的 li 元素了,看起來好像很繁瑣的樣子,事實上咱們由於能夠對makeevent傳入參數來改變返回的函數的一些特色。這好像面向對象的工廠模式對吧。事實上咱們徹底能夠用閉包來實現面向對象的封裝。

關於this

我在第二個事件函數裏使用了this,它表示了響應這個事件的當前對象,也即表示當前的列表項。而且在必要時還能夠往Lis[i]元素對象里加入其餘東西,以下:

      Lis[i].index = i;
      Lis[i].hehe = "hehe";
      Lis[i].onmouseout = function() {
        console.log(this.hehe);
        Lis[this.index].className = "";
      }

這樣寫也是能夠的。經過對象自己能夠動態的添加屬性這一個JavaScript的特色來完成的。

是的,JavaScript真是一門神奇的語言

相關文章
相關標籤/搜索