这里的闭包是啥意思?

闭包

这里的闭包我不是很理解

它是如何产生的?
以及为什么可以用更多的闭包来解决它?

阅读 4.4k
5 个回答

MDN 关于闭包的解释:

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

前半句有些许拗口,我的理解就是 一个函数 + 使用到自由变量自由变量 指的是既不是这个函数的参数也不是这个函数的局部变量的变量。所以后半句说只要创建一个函数,闭包就会被创建。即 从技术角度说,所有的函数都是闭包。

因此例子中 onfocus 的事件处理函数是一个闭包。

原示例没有达到预期效果的原因是因为 var 声明的 item 变量不存在块作用域,会 变量提升,相当于在 setupHelp 函数的顶层定义了一样,所以当循环结束后,item 指向的是最后一项。所以要解决这个问题,最快的就是改用 let 声明。至于 更多闭包,其实就是让每个事件处理函数访问到当前下标的 item,即每次循环都创建额外一个函数去 提供自由变量

说一下我的理解
先说产生,代码的满足闭包产生的条件
1.方法中存在函数的嵌套,也就是setupHelp嵌套了匿名函数
2.内层函数使用外层函数的变量,也就是item(这边由于使用的是var所以item被上升到了setupHelp的环境)
3.外层函数返回内层函数的引用。最终匿名函数被赋给了onfocus,达到了这个效果

可以这样的解决的原因是通过函数工厂,让所有的回调不再共享同一个词法环境。
修改后的方法中的环境是变为了makeHelpCallback内部

for (var i = 0; i < helpText.length; i++) {
    // 这个 item 被提升到 setupHelp 了,导致循环每执行一次,
    // item 就要被重新赋值一次
    var item = helpText[i];
    
    document.getElementById(item.id).onfocus = function() {
      // 由于 item 被重新赋值了,这里被执行的时候,访问到的是 item 
      // 的最后一个值 helpText[helpText.length - 1]
      // 而不是所期望的 helpText[i]
      showHelp(item.help);
    }
  }

至于所谓闭包,其实就是引擎发现你有个尚未执行的函数里引用了外头的 item,于是很贴心地帮你把这个 item 所在的“区域”也保存下来了,你这个尚未执行的函数和被保留的“区域”合起来,就成了一个“闭包”。

闭包就是函数+词法环境

闭包可以理解为:一个函数及其周围封闭词法环境中的引用构成闭包。可能这句话还是不好理解,
示例:

function createAction() {
    var message = "封闭环境内的变量";
    
    return function() {
        console.log(message);
    }
}

const showMessage = createAction();
showMessage();    // output: 封闭环境内的变量

更加详细的可以看这个大佬写的文章:还搞不懂闭包算我输(JS 示例)

推荐问题
宣传栏