Open
Description
JS深入浅出 - 闭包
参考文章:
★ JavaScript深入之闭包
概念
以下是不同网站、书籍对闭包的定义。
- 一个内部函数保持着其对外部函数词法作用域的访问权限。
- 一个函数对其周围状态(词法环境)的引用, 与该函数捆绑在一起的组合称为闭包。
- 保护一个可重用的局部变量的词法结构。
- 当函数可以记住并访问所在的词法作用域时, 就产生了闭包, 即使函数是在当前词法作用域之外执行。(《你不知道的JavaScript》)
- 闭包是指那些能够访问自由变量的函数。(MDN)
自由变量:在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。
简单理解:闭包就是一个持有对其外部函数词法作用域访问权限的函数。
闭包 = 函数 + 持有外部变量的访问权限
闭包分析
当你仔细理解了 JS 的执行上下文后,你就会发现:闭包其实是 JS 运行执行上下文时的产物!
看例子!
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();
JS 引擎在执行上述代码时的简要过程:
- 进入全局代码,创建全局执行上下文,全局执行上下文入栈。
- 全局执行上下文创建(变量对象,作用域链,this 对象),执行全局执行上下文。
- 执行到 checkscope 函数时,创建 checkscope 函数执行上下文,checkscope 执行上下文压入栈顶。
- checkscope 执行上下文初始化,创建变量对象、作用域链、this等。
- checkscope 函数执行完毕,checkscope 执行上下文从栈顶弹出。
- 执行 foo 函数,创建 foo 函数执行上下文,foo 执行上下文入栈。
- foo 执行上下文初始化,创建变量对象、作用域链、this等。
- foo 函数执行完毕,foo 函数上下文从执行上下文栈顶弹出。
Question:
当 foo 函数执行的时候,checkscope 函数上下文已经被销毁(即从执行上下文栈中被弹出),怎么还会读取到 checkscope 作用域下的 scope 值呢?
Answer:
依赖于作用域链!
我们知道 foo 执行上下文维护了一个作用域链:
fContext = {
Scope: [AO, checkscopeContext.AO, globalContext.VO],
}
就是因为这个作用域链,foo 函数依然可以读取到 checkscopeContext.AO 的值,说明当 foo 函数引用了 checkscopeContext.AO 中的值的时候,即使 checkscopeContext 被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO 活在内存中,foo 函数依然可以通过 foo 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。
闭包应用
闭包常用于保存或保护变量的访问权限。
- 私有变量
- 回调与计时器
- 绑定函数上下文
- 偏应用函数
- 函数重载: 缓存记忆 / 函数包装
- 即时函数: 独立作用域 / 简介代码 / 循环 / 类库包装 / 通过参数限制作用域内名称
缺点: 闭包信息始终会保存在内存里。