以下为本人学习所记,若有不对,望指出。

1. 变量的作用域

根据作用域的不同,JavaScript 中的变量可分为两种:全局变量局部变量

其中,函数内部可以直接读取全局变量

1
2
3
4
5
var a = 1;
function fn1() {
console.log(a);
}
fn1(); // 1

但是,函数外部不能读取函数内的局部变量

1
2
3
4
function fn2() {
var b = 2;
}
console.log(b); // ReferenceError: b is not defined

定义局部变量时要注意,要使用关键字varconstlet声明,若像下面这样声明,相当于window.b = 2,声明了一个全局变量!

1
2
3
4
5
function fn2() {
b = 2; // 不要这样声明,这样相当于声明了全局变量
}
fn2();
console.log(b); // 2

2. 引入闭包

那么如何才能在函数外部访问到函数内部的变量呢?

只需在函数内部,再定义一个函数,并将其作为返回值,返回一个函数:

1
2
3
4
5
6
7
8
9
function fn1() {
var a = 1;
return function fn2() {
console.log(a);
};
}
// 因为fn1函数有返回值,用result接收
var result = fn1();
result(); // 1

这样,在函数fn1外部,执行函数result,访问到了函数fn1内部的变量a

这就形成了一个简单的闭包。上例中fn2函数其实就是一个闭包函数。

闭包其实就是一个可以访问其他函数内部变量的函数。

3. 闭包的作用

从上例可看出,闭包可以在函数外部访问到函数内部的变量,这是其第一个作用。

闭包另一个作用,使已经运行结束的函数上下文中的变量对象继续留在内存中。看下面一段 demo:

1
2
3
4
5
6
7
8
9
10
function fn1() {
var a = 1;
return function fn2() {
console.log(a++);
};
}
var result = fn1();
result(); // 1
result(); // 2
result(); // 3

a是函数fn1中的局部变量,a的值在函数fn2中改变,fn2每执行一次,a+1

而上述代码执行 3 次result()后,分别输出了a的值,a不断地+1。这说明函数fn1中的变量a一直保存在内存中,并没有在函数fn1调用后被清除。

因为函数fn2被赋给了一个全局变量,因此fn2会一直在内存中,而fn2的存在依赖于fn1,所以fn1也一直保存在内存中,并不会调用后被垃圾回收机制清除。

4. 闭包的副作用

❗ 内存消耗

闭包会使得函数中的变量都被保存在内存中,内存消耗很大,不能滥用闭包,否则会造成严重的性能问题,在 IE 中可能导致内存泄露

解决方法:在退出函数之前,将不使用的局部变量全部清除。

❗ 误改函数内部的值

不要随便改变父函数内部变量的值。


参考文章学习 Javascript 闭包(Closure)