Skip to content

闭包的理解

闭包的定义

维基百科

  • 闭包 Closure,又称词法闭包或函数闭包
  • 是在支持头等函数的编程语言中,实现词法绑定的一种技术
  • 闭包在实现上是一个结构体,它存储了一个函数和一个关联的环境
  • 闭包跟函数最大的区别在于 当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样的话,及时脱离捕捉时的上下文,它也能正常运行

MDN

  • 闭包是由捆绑起来(封闭的)的函数和函数周围状态(词法环境)的引用组合而成。
  • 换言之,闭包让函数能访问它的外部作用域。
  • 在 JavaScript 中,闭包会随着函数的创建而同时创建。

个人理解

  • 对于一个普通函数,若他能访问外层作用域的自由变量,则这个函数结构就可以称为闭包
  • 广义来看,JS 的函数都是闭包,因为在全局中的函数也在访问全局作用域的自由变量
  • 狭义来看,一个访问了外部作用域自由变量的函数(组成的结构)就是一个闭包
  • 闭包是一种结构

作用域/自由变量域是在编译时,就确定的

我的问题

  1. 所谓编译时,指的是 parse 在进行词法分析的时候吗?

闭包的最简示例分析

ES1-3

ShiQVq

ES 新-结合执行上下文、环境记录分析(纯属个人理解)

代码:

javascript
function foo() {
  var age = 18;
  return function bar() {
    console.log(age);
  };
}

var fn = foo();
fn(); // 18

分析:

  • 在 v8 开始后,引擎会初始化代码执行所需要的环境,比如 ECS、内存、全局对象...

复习:全局对象的创建是在进入任何执行上下文之前

  • 创建全局执行上下文&全局环境记录,全局执行上下文中有执行上下文所对应的那些组件,词法环境、变量环境与全局环境记录相关联
  • 解析遇到 foo 函数定义,会创建 foo 函数的函数对象,执行到var fn = foo() ,在执行前夕,会创建函数环境记录 & 函数执行上下文

HIkBjt

  • 开始执行 foo, var age = 18,在 foo 的执行上下文中找到 age 变量,将值改为 18,然后执行到 return,遇到了 bar 函数声明,就会在内存中创建 bar 函数对象 & 创建 bar 函数环境记录,将 bar 函数 return

6hBoMz

  • return 后 foo 函数执行完成,return bar 函数,foo 执行上下文销毁,此时正常来讲 foo 函数环境记录也应该销毁,但是由于 bar 函数环境记录中的 outerEnv 指向 foo 函数环境记录,所以 foo 函数环境记录未被销毁
  • 然后 给 fn 赋值,在全局执行上下文中找到 fn, 赋值为 bar
  • fn 执行,创建 bar 函数执行上下文,执行到打印 age,先在自己执行上下文对应的环境记录中寻找,找不到通过 outerEnv 找到 foo 函数环境记录,找到变量 age FYYP9H

闭包的应用场景

函数封装

柯里化

数据私有

内存泄漏 & 防止

内存泄漏防止:

javascript
function foo() {
  var age = 18;
  return function bar() {
    console.log(age);
  };
}

var fn = foo();
fn(); // 18

// 防止内存泄漏
fn = null;

made with ❤️ by ankang