前言
闭包是一个复杂的概念,要掌握闭包,需要对JS的编译要有所了解,可以参考:
链接: JavaScript中的代码是怎么编译执行.
链接: JavaScript中的全局变量以及局部变量.
一、闭包是什么?
闭包(closure)是指有权访问另一个函数作用域中的变量的函数。 ----JavaScript高级程序设计
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包”。 ----JavaScript权威指南
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。 ----你不知道的JavaScript
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。 ---- MDN官方文档
简单理解就是:一个作用域可以访问另一个函数内部的局部变量。
例1、看下面的例子:
function f1() {
var a = 2;
function f2() {
console.log(a); // 2
}
f2();
}
f1();
闭包:对f2()
这个函数作用域,访问了另一个函数 f1()
里面的局部变量a
,就产生了闭包。
断点调试:
- 从
f1()
开始执行:
- 进入局部作用域:
- 执行
f2()
:
此时可以看见,已经产生了闭包f1()
。 - 展开来看,可以看见闭包
f1()
中的内容为a : 2
。
由此可知,在f2()
函数内部访问了f1()
内部的局部变量a
,此时f1()
是一个闭包函数(局部变量所在的函数就是闭包函数)。链接: 代码地址.
- 引用小黄书上的话:
从技术上来讲,这是闭包,但根据定义,确切来说这并不是闭包。
从纯学术的角度说,在上面的代码片段中,函数f2()
中具有一个涵盖f1()
作用域的闭包(事实上,涵盖了它能访问的所有作用域,比如全局作用域)。也可以认为f2()
封闭在了f1()
的作用域中,因为f2()
嵌套在f1()
内部。
例2、再看下面的例子,清晰的展示了闭包:
function f1() {
var a = 2;
function f2() {
console.log(a);
}
return f2;
}
var f = f1();
f(); // 2
与例1一样,函数 f2()
的词法作用域可以访问 f1()
的内部作用域。
- 在这个例子中,
f2()
函数本身当做一个值类型进行传递,并且将f2()
所引起的函数对象本身当做返回值。 - 来看一下
f1()
的执行:
f1()
执行后,其返回值(也就是内部的f2()
函数)赋值给变量f
并调用函数f()
,实际上只是通过不同标识符的引用调用了内部函数的函数f2()
。
- 此时可以看见,
f2()
显然可以正常被执行。
- 它(
f2
)在自己定义的词法作用域之外的地方(f1()
)执行。 - 你也可以自己调试.
关于垃圾回收机制:
- 原本
f1()
在执行后,通常在f1()
整个内部的作用域都会被销毁,这是因为引擎有垃圾回收机制来释放内存。 - 然而在闭包中,
f1()
内部作用域依然存在,并没有被回收(是因为仍有函数在使用这个作用域)。
– 而使用这个内部作用域的正是函数f2()
。
结论1: f2()
依然持有对f1()
作用域的引用,这个引用就叫做闭包。
结论2: f1()
就是这个闭包函数。
二、闭包的作用
- 以例2为例,闭包可以实现
f1()
外部的作用域,访问到f1()
内部作用域。(而在常规函数中这样是不可以的) - 例2与例1不同之处在于:例2实现了外部函数
f()
访问到了f1()
的内部变量。 - 例2本质上相当于执行了:
var f = function f2() {
console.log(a)
}
例2的代码,效果等同于下面:例3:
function f1() {
var a = 2;
return function f2() {
console.log(a);
}
}
var f3 = f1();
f3(); // 2
例3的执行,相当于执行 f3()
时,直接执行 f2()
并且访问的作用域是 f1()
的作用域。
- 因此,可以知道:
f1()
外面的作用域(f3()
)可以访问f1()
内部的局部变量(a = 2
)。 - 这里可以讨论一下
return
的作用:
当需要全局作用域(f3()
)需要访问局部作用域f1()
时,return
的作用就体现出来了。
*这等同于外部作用域访问内部作用域。而例1是内部作用域访问外部作用域。
结论:闭包的主要作用:延伸了变量的作用范围。
总结
这里对文章进行总结:
理解闭包,闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来。这显然类似于面向对象编程。在面向对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。
因此,通常你使用只有一个方法的对象的地方,都可以使用闭包。
不断学习,共同进步,希望文章对你有所帮助!