js循环中使用闭包

文章发布于 2023-05-10

js闭包概念

闭包就是在函数内部创建一个函数,内部函数可以访问函数的作用域。闭包会随着函数的创建而创建。

举例说明:

function init() {
  var name = "Mozilla"; // name 是一个被 init 创建的局部变量
  alert('执行的init函数')
  return function() { // 匿名的内部函数,一个闭包
        // 闭包可以访问外部函数的作用域
      alert(name); // 使用了父函数中声明的变量
  }
}
init(); 执行init函数,创建了匿名闭包函数,但是没有执行
init()();执行init函数,并且执行了匿名闭包函数

闭包的作用

  • 函数内的变量不受外部的干扰。形成不销毁的栈内存。
  • 把函数中的值保存下来。
  • 闭包可以实现方法和属性的私有化。

js循环中使用闭包

1. forEach与js闭包

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

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];
    
  helpText.forEach(function(item){
    document.getElementById(item.id).onfocus = function(){
      showHelp(item.help)
    }
  })

}

2. for循环与js闭包

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

//创建一个单独的词法作用域,
function a(item){
    return function(){
        console.log(item)
        showHelp(item.help)
    }
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];
    
  // onfocus事件会创建一个闭包,使用的同一个词法作用域。	
  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = a(item)
  }
}

setupHelp();

3. 定时器与js闭包

function a(i){
  return function(){
    console.log(i)
  }
}
for(var i=0;i<5;i++) {
  var fn = a(i) //每个定时器都创建一个独立的词法作用域
  setTimeout(fn,1000)
}

4. let和const与js闭包

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

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  // 使用let或者const定义item之后,item会转换为块级作用域。从而无需使用闭包来实现。
  for (let i = 0; i < helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function(){
      showHelp(item.help);
    }
  }

}

setupHelp();

错误的js闭包

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


function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  // 错误的js闭包。
  /** 
   * 错误原因:循环创建的闭包在同一个词法作用域下。在这个作用域中存在一个变量 item。
   * 这是因为变量 item 使用 var 进行声明,由于变量提升,所以具有函数作用域。
   * 当 onfocus 的回调执行时,item.help 的值被决定。
   * 由于循环在事件触发之前早已执行完毕,变量对象 item(被三个闭包所共享)已经指向了 helpText 的最后一项。
  **/

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    console.log(i) // 先遍历了三次。item为helpText最后一个
    document.getElementById(item.id).onfocus = function(){ //回调
      showHelp(item.help)
      console.log("我是onfocus事件执行")
    }
  }
}

setupHelp();

代码已经上传到 https://gitee.com/usuing/lession.git,喜欢可以star一下。