本文翻译自 w3schools:
- 原文地址:https://www.w3schools.com/js/js_function_closures.asp
- 译文地址:shixinzhang 的博客
JavaScript 中的变量有两种:
- 全局变量
- 局部变量
使用 闭包 我们可以将全局变量变为局部变量。
两种变量
一个函数可以访问它内部定义的变量,比如这样:
代码语言:javascript复制function myFunction() {
var a = 4;
return a * a;
}
同时函数也可以访问它外部定义的变量,比如这样:
代码语言:javascript复制var a = 4;
function myFunction() {
return a * a;
}
第一个例子中,a 是一个 局部变量。
局部变量只能在创建它的函数内使用,其他范围(其他函数等)都访问不到它。
在第二个例子中,a 是一个 全局变量。
在网页中,全局变量都属于 window 对象。
全局变量可以被当前页面的所有脚本使用或者修改。
和 Java 中相同的是: 全局变量和局部变量即使名称相同,也是不同的变量,修改其中一个不会影响另一个。
注意,不使用关键字
var
,直接创建的变量,永远是全局变量,哪怕它是在函数中创建的!
变量的生命周期
全局变量的生命周期和创建它的网页/ window 声明周期一致。
局部变量则短一些,它在函数调用(不是创建)时创建,在函数结束时被删除。
计数器的困境
假设你想要用一个变量计数,你想要让所有函数都可以使用这个计数器。
你可以使用一个全局变量,然后提供一个方法来增加它:
代码语言:javascript复制var counter = 0;
function add() {
counter = 1;
}
add();
add();
add();
// 现在计数器的值是 3
我们想要的效果是只能通过 add()
方法增加计数器的值。
但由于 counter
是全局变量,当前页面的脚本,不使用 add()
也能修改它。
那我们就把 counter
声明为局部变量,这样所有人要使用它只能通过 add()
方法:
function add() {
var counter = 0;
counter = 1;
}
add();
add();
add();
// 我们想要的效果是 counter 等于 3,但它并没有这样
每次调用 add()
方法,它都会将 counter
设置为 1.
JavaScript 的内部函数可以解决这个问题。
JavaScript 的嵌套函数
在 JavaScript 中,所有的函数都可以访问全局变量,除此外,它们还可以访问 “上一级函数” 中声明的变量(类似 Java 内部类)。
代码语言:javascript复制function add() {
var counter = 0;
function plus() {counter = 1;}
plus();
return counter;
}
在上面的例子中,内部函数 plus()
可以访问父函数的 counter
变量。
现在我们有了局部变量,也有了内部函数,只要能在最外部范围访问内部函数 plus()
,我们就能逃离计数器的困境了。
哦对了,我们还需要只初始化一次 counter
。
我们需要使用闭包。
JavaScript 的闭包
还记得自调用函数 IIFE (Immediately Invoked Function Expression)吗?它做了什么?
代码语言:javascript复制var add = (function () {
var counter = 0;
return function () {return counter = 1;}
})();
add();
add();
add();
// 现在 counter 的值就是 3
上述代码,先创建了一个自调用匿名函数,这个函数在创建时就进行了自调用,完成了 counter
的初始化。
然后将函数 {return counter = 1;}
赋值给 add
变量,add 就变成了函数。
关键的部分在于:add()
方法可以访问父函数声明的 counter
变量。
counter
被匿名函数的作用域保护着,我们只能通过 add()
方法修改它。
这就是闭包,它让函数可以拥有“私有”变量。
闭包就是一个函数即使在父函数关闭之后,也可以访问父函数中的变量。