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

贊助商

分類目錄

贊助商

最新文章

搜索

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

作者:admin    時(shí)間:2022-6-3 13:12:23    瀏覽:

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

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

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

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

完整HTML

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

</head>
<body>
<p id="help">點(diǎn)擊輸入框時(shí),這里顯示提示信息</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)點(diǎn)擊輸入框時(shí),顯示對(duì)應(yīng)的提示信息。

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

 

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

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

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

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

我們可以通過(guò)幾種方法來(lái)解決這個(gè)問(wèn)題。

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

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

完整HTML

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>
</head>
<body>
<p id="help">點(diǎn)擊輸入框時(shí),這里顯示提示信息</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)不再共享同一個(gè)環(huán)境, makeHelpCallback 函數(shù)為每一個(gè)回調(diào)創(chuàng)建一個(gè)新的詞法環(huán)境。在這些環(huán)境中,help 指向 helpText 數(shù)組中對(duì)應(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">點(diǎn)擊輸入框時(shí),這里顯示提示信息</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)項(xiàng)的 item 與事件回調(diào)相關(guān)聯(lián)起來(lái)
  }
}

setupHelp();

</script>

</body>
</html>

demodownload

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

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

如果不想使用過(guò)多的閉包,你可以用 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">點(diǎn)擊輸入框時(shí),這里顯示提示信息</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ù)期的。這個(gè)例子使用let而不是var,因此每個(gè)閉包都綁定了塊作用域的變量,這意味著不再需要額外的閉包。

解決方法四:使用forEach遍歷

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

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>
 
</head>
<body>
<p id="help">點(diǎn)擊輸入框時(shí),這里顯示提示信息</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é)

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

相關(guān)文章

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