闭包

99 阅读2分钟

这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

闭包

什么是闭包?

一个函数和对其周围状态的引用绑定再一起,这样的组合就是闭包。通常我们会讲:闭包是一个可以访问外部作用域的内部函数,即使这个外部作用域已经执行结束。

一般情况下,都是由一个内部函数的执行,会在执行的过程中访问到它的外部函数。每创建一个函数,闭包就会在函数创建的同时被创建出来。

function init() {
  let a = 1
  return function() {
    console.log(a)
  }
}
init()

闭包的使用中,我们可以利用它们的特性坐一些数据的隐藏和封装。

实例1:

例如下边这个例子

image.png

我们想得到的结果是,每点击一个,就改变?为当前的位数。例如点击第一个为1,点击第二个为2等等

js代码如下

function changeText(id,text) {
  document.getElementById(id).innerHTML = text;
}

function setupChange() {
  var textList = [
      {'id': '1', 'text': '当前点击的是第1位'},
      {'id': '2', 'text': '当前点击的是第2位'},
      {'id': '3', 'text': '当前点击的是第3位'},
      {'id': '4', 'text': '当前点击的是第4位'},
    ];

  for (var i = 0; i < textList.length; i++) {
    var item = textList[i];
    document.getElementById(item.id).onclick = function() {
      changeText(item.id, item.text);
    }
  }
}
setupChange();

这里我们发现一个问题,当前的代码,无论是点击哪个,都只有最后一个改变并且为4。这里是因为作用域的关系,我们最后函数中的changeText中的id和text都为最后一个,因为用的var。

那么修改方式有以下几种

修改方法1

使用let,使用var产生的是全局作用域,let是块级作用域,

修改如下:

let item = textList[i];

修改方法2

我们在修改下事件的调用,让回调不再共享同一个环境

修改如下:

function makeChangeBack(id, text) {
  return function() {
    changeText(id, text);
  };
}

document.getElementById(item.id).onclick = makeChangeBack(item.id, item.text);

makeChangeBack就为我们的事件产生了一个个不同的语法环境

修改方法3

使用匿名的闭包

修改如下:

(function() {
       var item = textList[i];
       document.getElementById(item.id).onclick = function() {
        changeText(item.id, item.text);
       }
})()

在这里循环了一个自执行的匿名函数

解决方法4

使用forEach

textList.forEach(function(item) {
    document.getElementById(item.id).onclick = function() {
        changeText(item.id, item.text);
      }
});

性能

一般而言,闭包在处理速度和内存消耗上是比较大的,在非特定环境,一般不是用闭包