技術(shù)頻道導(dǎo)航
HTML/CSS
.NET技術(shù)
IIS技術(shù)
PHP技術(shù)
Js/JQuery
Photoshop
Fireworks
服務(wù)器技術(shù)
操作系統(tǒng)
網(wǎng)站運(yùn)營

贊助商

分類目錄

贊助商

最新文章

搜索

[5個示例]介紹在循環(huán)中創(chuàng)建閉包的常見錯誤及解決方法

作者:admin    時間:2022-6-3 13:12:23    瀏覽:

在循環(huán)中創(chuàng)建閉包,很容易出現(xiàn)問題,本文將通過5個示例,介紹在循環(huán)中創(chuàng)建閉包的常見錯誤,以及如何使用正確的方法。

介紹在循環(huán)中創(chuàng)建閉包的常見錯誤及解決方法

在循環(huán)中創(chuàng)建閉包的常見錯誤

在循環(huán)中有一個常見的閉包創(chuàng)建問題,我們先來看看例子。

完整HTML

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>

</head>
<body>
<p id="help">點擊輸入框時,這里顯示提示信息</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>

<script type="text/javascript">

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': '你的Email'},
      {'id': 'name', 'help': '你的名字'},
      {'id': 'age', 'help': '你的年齡'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

</script>

 

</body>
</html>

demodownload

示例的預(yù)期是,當(dāng)點擊輸入框時,顯示對應(yīng)的提示信息。

不過執(zhí)行結(jié)果卻不符合預(yù)期,點擊輸入框時,提示信息并不跟著變化。無論焦點在哪個input上,顯示的都是關(guān)于年齡的信息。

 

出現(xiàn)這個原因是在循環(huán)中使用的閉包有問題。

數(shù)組 helpText 中定義了三個有用的提示信息,每一個都關(guān)聯(lián)于對應(yīng)的文檔中的 input 的 ID。通過循環(huán)這三項定義,依次為相應(yīng)input添加了一個 onfocus 事件處理函數(shù),以便顯示幫助信息。

運(yùn)行這段代碼后,你會發(fā)現(xiàn)它沒有達(dá)到想要的效果。無論焦點在哪個input上,顯示的都是關(guān)于年齡的信息。

原因是賦值給 onfocus 的是閉包。這些閉包是由他們的函數(shù)定義和在 setupHelp 作用域中捕獲的環(huán)境所組成的。這三個閉包在循環(huán)中被創(chuàng)建,但他們共享了同一個詞法作用域,在這個作用域中存在一個變量 item。這是因為變量item使用 var 進(jìn)行聲明,由于變量提升,所以具有函數(shù)作用域。當(dāng)onfocus的回調(diào)執(zhí)行時,item.help的值被決定。由于循環(huán)在事件觸發(fā)之前早已執(zhí)行完畢,變量對象item(被三個閉包所共享)已經(jīng)指向了helpText的最后一項。

我們可以通過幾種方法來解決這個問題。

解決方法一:使用更多的閉包

解決這個問題的一種方案是使用更多的閉包:特別是使用前面所述的函數(shù)工廠。

完整HTML

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>
</head>
<body>
<p id="help">點擊輸入框時,這里顯示提示信息</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>

<script type="text/javascript">

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': '你的Email'},
      {'id': 'name', 'help': '你的名字'},
      {'id': 'age', 'help': '你的年齡'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();

</script>

</body>
</html>

demodownload

運(yùn)行結(jié)果

 

這段代碼可以如我們所期望的那樣工作。所有的回調(diào)不再共享同一個環(huán)境, makeHelpCallback 函數(shù)為每一個回調(diào)創(chuàng)建一個新的詞法環(huán)境。在這些環(huán)境中,help 指向 helpText 數(shù)組中對應(yīng)的字符串。

解決方法二:使用匿名閉包

另一種方法是使用匿名閉包。

完整HTML

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>
</head>
<body>
<p id="help">點擊輸入框時,這里顯示提示信息</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>

<script type="text/javascript">

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': '你的Email'},
      {'id': 'name', 'help': '你的名字'},
      {'id': 'age', 'help': '你的年齡'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    (function() {
       var item = helpText[i];
       document.getElementById(item.id).onfocus = function() {
         showHelp(item.help);
       }
    })(); // 馬上把當(dāng)前循環(huán)項的 item 與事件回調(diào)相關(guān)聯(lián)起來
  }
}

setupHelp();

</script>

</body>
</html>

demodownload

執(zhí)行結(jié)果

解決方法三:使用let關(guān)鍵詞

如果不想使用過多的閉包,你可以用 ES2015 引入的 let 關(guān)鍵詞,在for循環(huán)內(nèi)把var改為let。參考文章:

完整HTML

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>
 
</head>
<body>
<p id="help">點擊輸入框時,這里顯示提示信息</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>

<script type="text/javascript">

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': '你的Email'},
      {'id': 'name', 'help': '你的名字'},
      {'id': 'age', 'help': '你的年齡'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

</script>

</body>
</html>

demodownload

執(zhí)行結(jié)果同樣是如預(yù)期的。這個例子使用let而不是var,因此每個閉包都綁定了塊作用域的變量,這意味著不再需要額外的閉包。

解決方法四:使用forEach遍歷

另一個可選方案是使用 forEach()來遍歷helpText數(shù)組并給每一個<p>添加一個監(jiān)聽器,如下所示:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>
 
</head>
<body>
<p id="help">點擊輸入框時,這里顯示提示信息</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>

<script type="text/javascript">

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': '你的Email'},
      {'id': 'name', 'help': '你的名字'},
      {'id': 'age', 'help': '你的年齡'}
    ];

  helpText.forEach(function(text) {
    document.getElementById(text.id).onfocus = function() {
      showHelp(text.help);
    }
  });
}

setupHelp();

</script>

</body>
</html>

demodownload

總結(jié)

本文通過5個示例,介紹了在循環(huán)中創(chuàng)建閉包的常見錯誤,以及如何使用正確的方法。

相關(guān)文章

標(biāo)簽: 閉包  
x
  • 站長推薦
/* 左側(cè)顯示文章內(nèi)容目錄 */