JavaScript变量分为全局变量和局部变量。局部变量可以用到闭包。
全局变量
函数可以访问函数内部定义的变量,例如:1
2
3
4function test{
var a = 4;
return a * a;
}
函数也可以访问外部定义的变量,例如:1
2
3
4var a = 4;
function test{
return a * a;
}
结果均为 : 16
- 在第一个实例中,
a
是一个局部变量。局部变量只能用于定义它的函数内部,对于其他的函数或脚本代码是不可用的。 - 在第二个实例中,
a
是一个全局变量。在web页面中全局变量属于window
对象。 - 全局变量和局部变量即便相同,它们也是两个不同的变量。修改其中一个,不会影响另一个的值。
变量声明时如果不使用
var
关键字,那么它就是一个全局变量,即便它在函数内部定义。
从外部读取局部变量
出于种种原因,我们有时候需要得到函数内的局部变量。但是,正常情况下是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数 :1
2
3
4
5
6function 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
9function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
闭包
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
1 | function f1() { |
在这段代码中,result
实际上就是闭包 f2
函数。它一共运行了两次,第一次的值是 999
,第二次的值是1000
。这证明了,函数 f1
中的局部变量 n
一直保存在内存中,并没有在 f1
调用后被自动清除。
为什么会这样呢?原因就在于 f1
是 f2
的父函数,而 f2
被赋给了一个全局变量,这导致 f2
始终在内存中,而 f2
的存在依赖于 f1
,因此f1
也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是 nAdd=function(){n+=1}
这一行,首先在 nAdd
前面没有使用 var
关键字,因此 nAdd
是一个全局变量,而不是局部变量。其次,nAdd
的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以 nAdd
相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
例1
1 |
|
变量 add
指定了函数自我调用的返回字值。自我调用函数只执行一次。设置计数器为 0
。并返回函数表达式。add
变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。计数器受匿名函数的作用域保护,只能通过 add
方法修改。
例2
1 | var name = "The Window"; |
通过 object.getNameFunc()
返回 function(){ return this.name; }
该匿名函数。但是这时候 this
指的是 window
全局对象,因此 this.name
指的是全局变量 var name = "The Window"
,所以输出的值为 The Window
。
例3
1 | var name = "The Window"; |
通过 object.getNameFunc()
返回 function(){ return that.name; }
该匿名函数。但是这时候 that
指的是 object
对象,因此 that.name
指的是局部变量 name : "My Object"
,所以输出的值为 My Object
。
参考自 :