JavaScript變量做用域與閉包實現

先拋出原問題:javascript

for (var i = 0; i < 10; i++) {
divs[i].onmouseover = function () {
    this.style.backgroundColor = "red";
    this.style.left = -55*i;
    };
}

測試結果顯示:鼠標通過全部div時,北京顏色會變成紅色,可是全部div的定位都爲-55*10!這就是問題所在!html

正好在JS閉包理解上還不是很通透,爲找出問題所在,特地寫了一個小小的demo:java

<!DOCTYPE html>
<html>
<body>
<div id="header">
<p id="p">This is a word!</p>
</div>
</body>
<script type="text/javascript">  
p.onclick = function() {
var header = document.getElementById('header');
var p = document.getElementById('p');
// 生成十個測試的span
for (var i = 0; i < 10; i++) {
    var ele_span = document.createElement("span");
    ele_span.id = "id_" + i;
    ele_span.innerHTML = "This is" + i + "span";
    header.appendChild(ele_span);
};
var spans= document.getElementsByTagName("span");
for (var i = 0; i < 10; i++) {
    spans[i].onmouseover = function () {
        console.log(i);
        console.log(this);
        this.style.backgroundColor = "red";
    }
    document.getElementById("id_"+i).onclick = function(i) {
        console.log(this);
        console.log(i);
        this.style.backgroundColor = "green";
    }
};
}
</script>
</html>

點擊段落,生成十個span,id從1~10,接着經過for循環爲每一個span綁定mouseover以及click事件,鼠標通過每一個元素時,背景顏色變成紅色,可是console.log(i)輸出永遠都是10。結果如圖:閉包

那試着把i經過參數傳遞到click綁定函數呢?狀況會怎樣?若是你認爲console.log(i)會輸出相應的i值,那你就被參數i騙了!參數i只是一個參數名,想寫成啥寫啥,不過通常的寫法都是e,覺得綁定事件,傳遞到時間函數的參數是事件自己,有圖有真相:app

這樣的話,想經過這樣把參數i值傳遞到時間函數裏是沒門了。這時候,閉包的功力就立體現了:函數

for (var i = 0; i < 10; i++) {
    document.getElementById("id_"+i).onclick = (function (i) {
        console.log(i);
    })(i);
};

測試結果以下:測試

從結果能夠看出,匿名函數自動執行,並且每次循環的i值傳遞到匿名函數裏,這樣就實現值傳遞了。this

畫個小圖理解思路:spa

JavaScript也採用此法做用域,函數執行依賴於變量做用域,這個做用域是在函數定義時決定的,而不是調用時決定的。局部變量做用域具備局部性的,當JavaScript須要查找變量i時,先查找本做用域,這裏就是先查找事件函數,若是沒有查找到,就往上查找外部對象,這裏就查找到for循環裏的變量,此時找到了,就取得i的值,但此時i的值已經自增到10,因此console.log(i)輸出10;JavaScript變量查找經過變量的做用域查找,不斷往頂層做用域查找,直到global對象,若是在global對象中尚未查找到變量,就會拋出引用錯誤(ReferenceError)異常。code

這就是JavaScript變量做用域的特性的,能夠經過閉包實現變量保存在函數做用域中。

相關文章
相關標籤/搜索