MDN对闭包的定义:
一个函数和对其周围(词法环境)的引用捆绑在一起(或者说函数被引用包围),这样一个组合就是闭包。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域
。
在 JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
结合上面的定义,我们可以得到如下公式:
闭包 = 函数 + 外层作用域
举个简单的例子,现在有一个test函数,在其函数内部访问一个外层作用域中的变量,这个行为就是闭包的引用。
var name = '闭包'
function test() {
console.log(name)
}
test()
在《Javascript权威指南》书中有这样一句话:「严格来讲,所以JavaScript函数都是闭包」,但这只是理论上的闭包,与我们平时使用的不太一样。上面这个例子只是一个简单的闭包。
从理论上来讲,所有函数都是闭包。因为它们在创建的时候就已经上层上下文的数据保存起来了。从实践上来讲:闭包应该满足两个条件:
- 在代码中引用了外层作用域的变量;
- 即使创建它的上下文已经销毁,它仍然存在;
闭包的应用
闭包的应用,绝大多是都是在维护内部变量的场景下使用。我们再看一段《JavaScript权威指南》上的代码:
let scope = 'global scope'
function checkscope(){
let scope = 'local scope'
function f(){
return scope
}
return f
}
let s = checkscope()
s() // 这里返回什么?
解析:
1.首先执行全局代码,创建全局执行上下文,定义全局变量scope并赋值
2.申明checkscope函数,并创建该函数的执行上下文,创建局部变量scope并赋值
3.申明f函数,创建该函数的执行上下文
4.执行checkscope函数,该函数又返回了一个f函数赋值给了变量s
5.执行s函数,相当于执行了f函数。这里返回的scope是local scope。至于为什么是local scope,词法作用的基本规则:JavaScript函数是使用定义它们的作用域来执行的。 在定义f函数的作用域中,变量scope的值为local scope
。
闭包的缺陷
- 由于闭包的存在可能会造成变量常驻内存,使用不当会造成内存泄漏;
- 内存泄漏可能会导致应用程序卡顿或崩溃;