JavaScript 闭包
JavaScript 闭包可以让一个内层函数访问到外层函数的作用域,让内层函数可以使用外层函数中定义的变量。
JavaScript 闭包可以让一个内层函数访问到外层函数的作用域,让内层函数可以使用外层函数中定义的变量。
局部变量与全局变量
函数可以使用函数内部定义的所有变量,如下所示:
function myFunction() {
let a = 4;
return a * a;
}
函数也可以访问函数外部定义的变量,如下所示:
let a = 4;
function myFunction() {
return a * a;
}
在第一个示例中,a
是局部变量。局部变量只能在定义它的函数内部使用。它对其他函数和其他脚本代码隐藏。
在第二个例子中,a
是一个全局变量,在浏览器环境中,全局变量属于 window 对象。页面(和窗口)中的所有脚本都可以使用(和更改)全局变量。
同名的全局变量和局部变量是不同的变量,修改一个并不影响另另一个。
没有使用关键字( var
、 let
、 或 const
)声明的变量总是全局的,即使它们是在函数内部创建的:
function myFunction() {
a = 4;
}
变量的生命周期
全局变量一直存在,直到页面被丢弃,例如当您导航到另一个页面或关闭窗口时。
局部变量的寿命很短。它们在调用函数时创建,并在函数完成时删除。
计数器的例子
假设您想使用一个变量来统计数量,并且您希望该计数器可用于所有函数。
您可以使用全局变量 counter
和一个用来增加计数器的函数:
// 计数器变量
let counter = 0;
// 增加计数器的函数
function add() {
counter += 1;
}
// 调用 3 次
add();
add();
add();
// counter 的值是 3
上面的方案有一个问题:由于 counter
是全局变量,页面上的任何代码都可以更改 counter
变量,而无需调用 add()。
我们需要 JavaScript 闭包来解决这个问题。
JavaScript 闭包
还记得自执行函数吗?这个函数有什么作用?
const add = (function () {
let counter = 0;
return function () {
counter += 1;
return counter;
};
})();
add();
add();
add();
// counter 的值是 3
示例说明:
自执行函数中将计数器 counter
初始化设置为 0 ,并返回一个函数表达式。该函数表达式中实现了对父函数(自执行函数)中的变量 counter
每次增加 1
,并返回当前的计数器的数值。
自执行函数定义后立刻执行,并返回的函数表达式。变量 add
被赋值为函数表达式,也就是说 add
是一个函数。
调用 add()
的时候,就会访问到函数表达式父函数中的 counter
变量,从而实现对计数器的修改。
这样 add 就变成了一个函数。“精彩”的部分是它可以访问父作用域中的计数器。
上面的这个方案成为 JavaScript 闭包。闭包让函数和其周围状态(counter
)的引用绑定在了一起,也就是闭包让你可以在一个内层函数中访问到其外层函数的作用域。
闭包的一个用处就是可以进行模块化的设计,它可以将部分的变量或者数据隐藏在模块的内部,而只暴露必要的操作,增加了模块的安全性。