feer.cc

V1

2022/11/13阅读:15主题:全栈蓝

什么是闭包?「前端每日一题v22.11.8」

什么是闭包?「前端每日一题v22.11.8」

问题

在javascript中,经常听见闭包,那什么是闭包,闭包有哪些用途?

概念

闭包就像它的字面意思一样,表示一个封闭的内存空间,就叫闭包。换言之,当每个函数被创建的时候,都有一个闭包。

在js中有全局变量和局部变量,在函数内部可以读取函数外部作用域,函数外部无法直接读取函数内部变量。因此一个函数可以形成一个闭包

代码

可以通过代码来具体展示一下什么是闭包

function init({
  var name = "init"// name 是一个被 init 创建的局部变量
  function alertName(// alertName() 是内部函数,一个闭包
      alert(name); // 使用了父函数中声明的变量
  }
  alertName();
}
init(); // 弹出init

最终弹出init,但是init函数内部的name变量在函数外部是访问不到的,相信大家都理解。这个没有什么问题,我们再来一个例子

function init({
  var name = "init";
  function alertName({
      alert(name);
  }
  return alertName;
}
var fun = init();
fun() // 同样弹出init

这个同样弹出init,同上一个例子的区别就是init函数将内部的alertName函数导出。闭包是由函数以及声明该函数的词法环境组合而成的,这个环境包含了闭包创建时作用域内的任何局部变量,这个例子中fun是init内部函数alertName的引用,alertName实例维持了对于name的引用,所以fun调用时,name仍然可用

深入

借用MDN上的内容,闭包有什么用呢?

我们可以定义一个公共函数,令其可以访问私有函数和变量

var Counter = (function({
  var num = 0;
  function change(val{
    num += val;
  }
  return {
    increfunction({
      change(1);
    },
    decrefunction({
      change(-1);
    },
    valuefunction({
      return num;
    }
  }
})();

console.log(Counter.value()); /* logs 0 */
Counter.incre();
Counter.incre();
console.log(Counter.value()); /* logs 2 */
Counter.decre();
console.log(Counter.value()); /* logs 1 */

这里我们使用自执行函数创建了一个词法环境,然后再利用闭包暴露出来到Counter上

当然我们也可以不通过自执行函数创建多个计数器

var makeCounter = function({
  var num = 0;
  function change(val{
    num += val;
  }
  return {
    increfunction({
      change(1);
    },
    decrefunction({
      change(-1);
    },
    valuefunction({
      return num;
    }
  }
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.incre();
Counter1.incre();
console.log(Counter1.value()); /* logs 2 */
Counter1.decre();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

Counter1和Counter2都是维护的自己的计数逻辑,都具有各自的独立性

闭包的缺点

闭包常见的一个问题就是for循环的问题,很多新手总是不理解以下代码输出什么

for (var i = 0; i < 4; i++) {
  setTimeout(function({
    console.log(i);
  }, 1000);
}
// 结果是4个4,1s过后,词法环境的i都已经变成了4,解决方法是使用let或者闭包

闭包会使函数变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成性能问题,IE中可能导致内存泄露。

那解决方法是什么呢?就是在退出函数之前,将不使用的局部变量删除

分类:

前端

标签:

前端

作者介绍

feer.cc
V1

微信公众号:FE情报局