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。
参考自 :