1:如何點擊某一個 li 的時候 alert 輸出其index?
<ul id="test">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
window.onload = function(){
var oLis = document.getElementById("test").getElementsByTagName("li");
for(var i = 0;i < oLis.length;i ++){
oLis[i].onclick = function(){
alert(i); //每次都是4
}
}
}
解析:因為在for循環里面指定給oLis[i].onclick的事件處理程序,也就是onclick那個匿名函數是在for循環執行完成后(用戶點擊時)才被調用的。而調用時,需要對變量i求值,解析程序首先會在事件處理程序內部查找,但i沒有定義,然后又到方法外部查找,此時有定義,但i的值是4(只有i大於4才會停止執行for循環),因此,就會取到該值——這正是閉包(匿名函數)要使用其外部作用域中變量的結果。而且,這也是由於匿名函數本身無法傳遞參數(故而無法維護自己的作用域)造成的.(比較啰嗦,可以看下面的解釋)
其實是根據作用域鏈的原理,在這個函數里面的i其實引用的是最后一次i的值,為什么不是0,1,2,3,...呢?因為在你for循環的時候,你並沒有執行這個函數,你這個函數是在你點擊的時候才執行的,當執行這個函數的時候,它發現它自己沒有這個變量i,於是向它的作用域鏈中查找這個變量i,因為當你單擊這個box的時候已經for循環完了,所以儲存在作用域鏈里面的i的值就是4,最后就彈出出來4了。
2:解決辦法:將每次for循環中的變量i保存到某個地方;
方法一:
window.onload = function(){
var oLis = document.getElementById("test").getElementsByTagName("li");
for(var i = 0;i < oLis.length;i ++){
(function(i){
oLis[i].onclick = function(){
alert(i); //0123
}
})(i);
}
}
解釋:成功打印每個 i 的值,原理就是通過自執行函數,將變量i保存到這個自執行函數的參數中。
方法二:
window.onload = function(){
var oLis = document.getElementById("test").getElementsByTagName("li");
for(var i = 0;i < oLis.length;i ++){
oLis[i].index = i;
oLis[i].onclick = function(){
alert(this.index); //0123
}
}
}
解釋:將每次循環得到的i,賦值給oLis[i]對象的index屬性,在通過this指向,取出點擊當前對象的index的值。
方法三:
window.onload = function(){
var oLis = document.getElementById("test").getElementsByTagName("li");
for(var i = 0;i < oLis.length;i ++){
oLis[i].onclick = (function(e){
return function(){
alert(e); //0123
}
})(i);
}
}
解釋:
3:解決辦法:讓這個函數直接執行;
window.onload = function(){
var oLis = document.getElementById("test").getElementsByTagName("li");
for(var i = 0;i < oLis.length;i ++){
oLis[i].onclick = a();
function a(){
alert(i); /0123
}
}
}
解釋:雖然這樣可以打印出每次變量i的值,但是我們沒有點擊li的時候他已經執行完了,也就是說,這個點擊事件已經可有可無了,所以我們這種方法在綁定事件中不可用。
4:ES6為我們新增了,定義變量的關鍵字 let
window.onload = function(){
var oLis = document.getElementById("test").getElementsByTagName("li");
for(let i = 0;i < oLis.length;i ++){
oLis[i].onclick = function(){
alert(i); //0123
}
}
}
解釋:let
允許聲明一個作用域被限制在塊級中的變量、語句或者表達式。與var關鍵字不同的是,它聲明的變量只能是全局或者整個函數塊的。
let的幾點用法:
1>用於聲明變量,不做變量提升;
2>在同一個作用域中,不允許重復聲明同一個變量;
3>會產生塊級作用域{ };
4>特殊注意:for循環是一個塊級作用域,for后{}是一個塊級作用域,for塊級作用域是for{}塊級作用域的父級作用域。
for(let i = 0;i < 5;i++,console.log( i )){ 0 1 2 3 4
let i = 10;console.log( i ); 10 10 10 10 10
}
alert(i);//報錯
循環執行了幾次,就存幾個塊級作用域,每一個塊級作用域都有一個變量 i ,但是這幾個i不是同一個i 。(函數內部的變量i 與 循環變量i 不在同一個作用域,有各自單獨的作用域)