首頁 > web前端 > js教程 > 主體

JS常見問題為什麼點擊彈出的i總是最後一個_javascript技巧

WBOY
發布: 2016-05-16 15:21:48
原創
1112 人瀏覽過

在前端群組裡看過很多人問過這個問題,今晚又有人問了這個問題,所以寫篇文章整理一下。首先看一下程式碼,點選li之後彈出當前li所對應的索引值。於是很多人刷刷刷寫了下面的程式碼。

var aLi = document.getElementsByTagName('li');
for(var i = 0; i < aLi.length; i++){
  aLi[i].onclick = function(){
    alert(i);
  }
}
登入後複製

  但結果不盡人意,為了簡單,我們約定頁面中有2個li。點選li之後彈出的都是2。
  我們先來分析為什麼結果是1.我們可以簡單的將循環分成兩部。

i = 0时,aLi[0].onclick = function(){alert(i)}
i = 1时,aLi[1].onclick = function(){alert(i)}
i = 2时,不满足条件跳出循环.
登入後複製

 在執行click的函數的時候,會有一個作用域鏈,這個作用域鍊是一個物件列表,這組物件定義了程式碼作用域中的變數。 (關於變數物件的內容想更詳細了解的可以查看變數物件。)當我們alert(i)的時候,會去從內到外的去尋找變數i。這時候這個函數的作用域鏈上有兩個對象,這時循環已經結束了,i此時的值為2.所以點擊任何一個li,彈出的都是2,而不是我們想要的索引值。重點在於彈出的是變數i,變數i,變數i。重要的事情說三遍。

 那麼問題來了,我們要如何解決這個問題。我們需要做的就是在每次給aLi[i]綁定事件的時候,將這個時候i的值保存在內部的作用域中。解決方案如下。

var aLi = document.getElementsByTagName('li');
for (var i = 0; i < aLi.length; i++) {
 (function(i){
   aLi[i].onclick = function () {
      alert(i);
     };
 })(i)
}
登入後複製

這裡牽涉到一個區塊級作用域的概念。在es6出來前,函數是作為創建區塊級作用域的主要手段。這裡我們透過在aLi[i].onclick外面套上一層函數,將i當作參數,我們重新分析一下結果。

 i = 0时,
  (function(i){
   aLi[0].onclick = function(){
     alert(i);
   }
  })(0)
  i = 1时,
  (function(i){
   aLi[1].onclick = function(){
     alert(i);
   }
  })(1)
  i = 2时,不满足条件跳出循环.
登入後複製

由於多了一層自執行函數的包裹,當我們點擊li時,會有三層的作用域,從內帶外分別是:click函數內部的變數對象,自執行函數的變數對象和最外層的window物件。查找到第二層的時候,找到了i,自執行函數的i等於傳入的參數值,相對應的存下了當時i的值,所以就彈出了對應的索引值。

下面再給大家分享一個js常見的問題,實作點擊li能夠彈出當前li索引與innerHTML的函數

點選其中一項需要alert出下列結果:

按照我們平常的想法,程式碼應該是這樣寫的:

var myul = document.getElementsByTagName("ul")[0];
  var list = myul.getElementsByTagName("li");
 
  function foo(){
    for(var i = 0, len = list.length; i < len; i++){
      list[i].onclick = function(){
        alert(i + "----" + this.innerHTML);
      }
    }
  }
  foo();
登入後複製

但是不巧的是產生的結果是這樣的: 


索引index為什麼總是4呢,這是js中沒有區塊級作用域導致的。這裡有三種解決思路

1. 使用閉包

<script type="text/javascript">
var myul = document.getElementsByTagName("ul")[0];
var list = myul.getElementsByTagName("li");
function foo(){
  for(var i = 0, len = list.length; i < len; i++){
    var that = list[i];
    list[i].onclick = (function(k){
      var info = that.innerHTML;
      return function(){
        alert(k + "----" + info);
      };
    })(i);
  }
}
foo();
</script>
登入後複製

2.使用ES6中的新特性let來宣告變數

用let來宣告的變數會具有區塊級作用域,很明顯可以達到要求,不過需要注意的是得加個'use strict'(使用嚴格模式)才會生效

<script type="text/javascript">
var myul = document.getElementsByTagName("ul")[0];
var list = myul.getElementsByTagName("li");
function foo(){'use strict'
  for(let i = 0, len = list.length; i < len; i++){
    list[i].onclick = function(){
      alert(i + "----" + this.innerHTML);
    }
  }
}
foo();
</script>
登入後複製

3.引入jquery,使用其中的on或delegate進行事件綁定(它們都有事件代理的特性)

<script type="text/javascript" src="jquery-1.8.2.min.js"></script>

<script type="text/javascript">
$("ul").delegate("li", "click", function(){
  var index = $(this).index();
  var info = $(this).html();
  alert(index + "----" + info);
});
</script>
<script type="text/javascript">
$("ul").on("click", "li", function(){
  var index = $(this).index();
  var info = $(this).html();
  alert(index + "----" + info);
});
</script>
登入後複製
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!