js闭包

JavaScript变量分为全局变量和局部变量。局部变量可以用到闭包。

全局变量

函数可以访问函数内部定义的变量,例如:

1
2
3
4
function test{
var a = 4;
return a * a;
}

函数也可以访问外部定义的变量,例如:

1
2
3
4
var a = 4;
function test{
return a * a;
}

结果均为 : 16

  • 在第一个实例中,a 是一个局部变量。局部变量只能用于定义它的函数内部,对于其他的函数或脚本代码是不可用的。
  • 在第二个实例中,a 是一个全局变量。在web页面中全局变量属于 window 对象。
  • 全局变量和局部变量即便相同,它们也是两个不同的变量。修改其中一个,不会影响另一个的值。

    变量声明时如果不使用 var 关键字,那么它就是一个全局变量,即便它在函数内部定义。

从外部读取局部变量

出于种种原因,我们有时候需要得到函数内的局部变量。但是,正常情况下是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数 :

1
2
3
4
5
6
function f1(){
var n = 999;
function f2(){
alert(n);
}
}

在上面的代码中,函数 f2 就被包括在函数 f1 内部,这时 f1 内部的所有局部变量,对 f2 都是可见的。但是反过来不行,f2 内部的局部变量,对 f1 就是不可见的。这就是js语言的“链式作用域”结构,子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然 f2 可以读取 f1 中的局部变量,那么只要把 f2 作为返回值,我们就可以在 f1 外部读取它的内部变量了。

1
2
3
4
5
6
7
8
9
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999

闭包

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

1
2
3
4
5
6
7
8
9
10
11
12
function f1() {
var n = 999;
nAdd = function () { n += 1 }
function f2() {
alert(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000

在这段代码中,result 实际上就是闭包 f2 函数。它一共运行了两次,第一次的值是 999,第二次的值是1000。这证明了,函数 f1 中的局部变量 n 一直保存在内存中,并没有在 f1 调用后被自动清除。

为什么会这样呢?原因就在于 f1f2 的父函数,而 f2 被赋给了一个全局变量,这导致 f2 始终在内存中,而 f2 的存在依赖于 f1,因此f1 也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是 nAdd=function(){n+=1} 这一行,首先在 nAdd 前面没有使用 var 关键字,因此 nAdd 是一个全局变量,而不是局部变量。其次,nAdd 的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以 nAdd 相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>计数器</title>
</head>
<body>

<p>局部变量计数。</p>
<button type="button" onclick="myFunction()">计数!</button>
<p id="demo">0</p>
<script>
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
function myFunction(){
document.getElementById("demo").innerHTML = add();
}
</script>

</body>
</html>

变量 add 指定了函数自我调用的返回字值。自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。add 变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。计数器受匿名函数的作用域保护,只能通过 add 方法修改。

例2

1
2
3
4
5
6
7
8
9
10
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());

通过 object.getNameFunc() 返回 function(){ return this.name; } 该匿名函数。但是这时候 this 指的是 window 全局对象,因此 this.name 指的是全局变量 var name = "The Window",所以输出的值为 The Window

例3

1
2
3
4
5
6
7
8
9
10
11
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());

通过 object.getNameFunc() 返回 function(){ return that.name; } 该匿名函数。但是这时候 that 指的是 object 对象,因此 that.name 指的是局部变量 name : "My Object",所以输出的值为 My Object


参考自 :

坚持原创技术分享,您的支持将鼓励我继续创作!