目录
什么是闭包
闭包是由函数和函数外部的作用域组成。 换句话说,定义在函数内部的子函数, 子函数和其外部局部变量组合起来就叫做闭包。
作用域(scope)
闭包,如果用白话文说,我会说: 闭包是一个封闭的包块。 封闭是指闭包内部的对象作用的范围有限。我们把这个有限的范围叫做作用域。
作用域由代码块、局部变量(又称语句块)组成。下面介绍代码块和局部变量的概念。
代码块
- 普通代码块(又称语句块)
1. 由{}或者if...else括起来的代码,可看作为代码块
{
var x = 2;
}
//或者
if(true) {
var x = 3
}
- 标记代码块
对于一块代码块,我们可以取一个名字,方便引用它。
someName: {
var x = 2;
}
局部变量
在代码块的顶部创建的变量,这样的变量叫做局部变量。局部变量的作用域 = 它的外部代码块+代码块下其他变量。
闭包的案例
了解闭包的两要素(局部变量和作用域)之后,我们来看使用闭包的真实案例
封装局部变量
function makeCounter(){ //创建计数器
let count = 0;
return function increase() {
return count++;
}
}
const counter = makeCounter();// 实例化闭包
counter() // 修改局部变量count的值
counter() // 修改局部变量count的值
console.log(counter())
//输出2
1. 外部函数makeCounter(): 由局部变量count和内部子函数increase()组成。 子函数increase和它的外部作用域 makeCount{}一起组成了闭包。
2. 词法环境。子函数Increase创建的时候,其内部会自动创建了一个Enviroment引用,指向外部作用域(包含了外层函数makeCounter的局部变量)。所以这里increate内部的语句:count++,它类似于enviroment.count ++;
延长变量生命周期
makeCounter()函数调用完, 其局部变量count,没有会被垃圾回收。 闭包函数counter持有局部变量,导致其生命周期将延长。
如果要清理局部变量,需要调用counter = null。
总结
从上面介绍的闭包的两个特性来看,闭包不正是面向对象中的封装思想吗? 封装由 {数据+方法}组成。个人认为JavaScript是函数式编程的产物,而闭包正是一种封装思想。所以如果想封装一些局部变量和方法,那么可以考虑用闭包来解决。
闭包避免了全局变量带来的污染,尽管它有内存泄漏的风险。
参考链接
https://segmentfault.com/a/1190000013243680https://segmentfault.com/a/1190000013243680