抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

#7 闭包

闭包其实就是一个可以访问其他函数内部变量的函数。创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以 访问到当前函数的局部变量。

img

因为通常情况下,函数内部变量是无法在外部访问的(即全局变量和局部变量的区别),因此使用闭包的作用,就具备实现了能在外部访问某个函数内部变量的功能,让这些内部变量的值始终可以保存在内存中。下面我们通过代码先来看一个简单的例子

1
function fun1() {	var a = 1;	return function(){		console.log(a);	};}fun1();var result = fun1();result();  // 1// 结合闭包的概念,我们把这段代码放到控制台执行一下,就可以发现最后输出的结果是 1(即 a 变量的值)。那么可以很清楚地发现,a 变量作为一个 fun1 函数的内部变量,正常情况下作为函数内的局部变量,是无法被外部访问到的。但是通过闭包,我们最后还是可以拿到 a 变量的值

闭包有两个常用的用途

  • 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,我们可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
  • 函数的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。

其实闭包的本质就是作用域链的一个特殊的应用,只要了解了作用域链的创建过程,就能够理解闭包的实现原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let a = 1
// fn 是闭包
function fn() {
console.log(a);
}

function fn1() {
let a = 1
// 这里也是闭包
return () => {
console.log(a);
}
}
const fn2 = fn1()
fn2()
  • 大家都知道闭包其中一个作用是访问私有变量,就比如上述代码中的 fn2 访问到了 fn1 函数中的变量 a。但是此时 fn1 早已销毁,我们是如何访问到变量 a 的呢?不是都说原始类型是存放在栈上的么,为什么此时却没有被销毁掉?
  • 接下来笔者会根据浏览器的表现来重新理解关于原始类型存放位置的说法。
  • 先来说下数据存放的正确规则是:局部、占用空间确定的数据,一般会存放在栈中,否则就在堆中(也有例外)。 那么接下来我们可以通过 Chrome 来帮助我们验证这个说法说法。

img

上图中画红框的位置我们能看到一个内部的对象 [[Scopes]],其中存放着变量 a,该对象是被存放在堆上的,其中包含了闭包、全局对象等等内容,因此我们能通过闭包访问到本该销毁的变量。

另外最开始我们对于闭包的定位是:假如一个函数能