【前置知识】
- JavaScript的作用域。
- 内存空间
什么是闭包?
函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。 ——MDN
:::info
词法环境:在JavaScript中,每个运行的函数,代码块 {...}
以及整个脚本,都有一个称为 词法环境 的内部(隐藏)的关联对象。由局部变量和外部代码构成。
:::
正常情况下一个 外部函数 无法访问并获取到 内部函数 的变量。
原因是 内部函数 的局部变量,外部是无法访问的,内部却可以访问外部的变量。
例子1:
一般情况下,函数内部可以直接访问全局变量。
function sayHi() {
let name = "Pete";
function fun2(){
console.log("Hi, " + name);
}
}
sayHi(); //显示“Hi,Pete”
另一方面,函数外部不可以访问函数内部的局部变量
function sayHi() {
console.log("Hi, " + name);
function fun2(){
let name = "Pete";
}
}
sayHi(); //显示“Hi,Pete”
所以,按我的理解,闭包就是一个作用域有权访问另一个作用域的函数。上面举例的函数 fun2
可以访问函数 sayHi
,那么函数 fun2
就是闭包。
所以,闭包的一个前提是闭包必须是个函数!
闭包的用途是什么?
假如没有闭包
假如没有闭包,也就没有局部变量和全局变量的区别(或者说所有的变量都是全局变量)代码所有的角落都可以获取到所有的变量,万一某个程序员接手了一个没有闭包的大项目,该程序员就必须考虑自己写的变量名有没有和旧的变量名冲突!
所以闭包的用途就是防止全局污染。通过隐藏变量,实现私有数据,减少全局变量,尽可能的减少全局污染。
用途
例子2:闭包,嵌套函数闭包(最常用的创建闭包方式)
function sayHi() {
let name1 = "John";
return function(){
console.log(name1)
}
}
let fun = sayHi()
fun() //打印出“John”
该解决办法是把内层函数作为外层函数的返回值!
闭包的缺点是什么?
看例子2,函数 sayHi
放在变量 fun
里面,执行 sayHi
后,只要变量 fun
一直存在,那么函数 sayHi
就不会得到释放,也就不会被浏览器垃圾回收,一直占用内存。
如果这个函数里面的变量不多,那么占用的内存可有可无,但是如果是DOM操作呢?
这种现象,就叫做内存泄漏,也叫内存溢出,闭包的一个缺点。
:::info
补充:
虽然闭包并不会造成内存泄露,真实原因是 JS 引擎的实现有问题,但网上已经以讹传讹了。所以面试时我们依然要答出这一点。
:::
【资料来源】 JS完美收官之——闭包 JavaScript.info—闭包