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

贊助商

分類目錄

贊助商

最新文章

搜索

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

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

在循環(huán)中創(chuàng)建閉包,很容易出現問題,本文將通過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

示例的預期是,當點擊輸入框時,顯示對應的提示信息。

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

 

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

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

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

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

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

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

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

完整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

運行結果

 

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

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

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

完整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);
       }
    })(); // 馬上把當前循環(huán)項的 item 與事件回調相關聯起來
  }
}

setupHelp();

</script>

</body>
</html>

demodownload

執(zhí)行結果

解決方法三:使用let關鍵詞

如果不想使用過多的閉包,你可以用 ES2015 引入的 let 關鍵詞,在for循環(huán)內把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í)行結果同樣是如預期的。這個例子使用let而不是var,因此每個閉包都綁定了塊作用域的變量,這意味著不再需要額外的閉包。

解決方法四:使用forEach遍歷

另一個可選方案是使用 forEach()來遍歷helpText數組并給每一個<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

總結

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

相關文章

標簽: 閉包  
x
  • 站長推薦
/* 左側顯示文章內容目錄 */