JS FAQ: クリックするとポップアップ表示される i が常に最後のものになるのはなぜですか_JavaScript ヒント

WBOY
リリース: 2016-05-16 15:21:48
オリジナル
1114 人が閲覧しました

フロントエンドグループでこの質問をしている人がたくさんいたのですが、今夜も同じ質問をする人がいたので、それを整理するために記事を書きました。まず、コードを見てください。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 になる理由を分析しましょう。ループは単純に 2 つの部分に分割できます。

i = 0时,aLi[0].onclick = function(){alert(i)}
i = 1时,aLi[1].onclick = function(){alert(i)}
i = 2时,不满足条件跳出循环.
ログイン後にコピー

クリック関数を実行すると、スコープ チェーンが作成されます。このスコープ チェーンは、コード スコープ内の変数を定義するオブジェクトのリストです。 (変数オブジェクトの内容を詳しく知りたい場合は、変数オブジェクトを表示できます。)alert(i) を実行すると、変数 i を内側から外側に向かって探します。この時点で、この関数のスコープチェーンには 2 つのオブジェクトがあり、ループは終了しており、このときの i の値は 2 です。したがって、任意の li をクリックすると、ポップアップが表示されます。 2、必要なインデックス値ではありません。重要なのは、ポップアップされるのは変数 i、変数 i、変数 i であるということです。大事なことは3回言いましょう。

そこで、この問題をどのように解決するかという問題が生じます。必要なのは、イベントを 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 が登場する前は、関数がブロックレベルのスコープを作成する主な手段でした。ここでは、i をパラメーターとして使用して、aLi[i].onclick の外側に関数のレイヤーを配置し、結果を再分析してみましょう。

 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 をクリックすると、内側から外側に向かって 3 つのレベルのスコープが存在します: クリック関数内の変数オブジェクト、クリック関数の変数オブジェクト。自己実行関数とレイヤーの最も外側のスコープ。 2 番目のレベルを検索すると、自己実行関数の i が渡されたパラメータ値と同じであることがわかり、そのときの i の値が対応して保存されるため、対応するインデックス値がポップアップ表示されます。

よくある js の問題を共有しましょう。li をクリックすると、現在の li インデックスと innerHTML 関数がポップアップします。

いずれかをクリックすると、次のアラートが表示されます:

通常の考え方によれば、コードは次のように記述する必要があります:

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();
ログイン後にコピー

しかし、残念なことに、結果は次のようになります:


インデックスが常に 4 なのはなぜですか? これは、js にブロックレベルのスコープがないことが原因です。解決策は 3 つあります

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 学習者の迅速な成長を支援します!