JavaScript闭包详解
闭包就是由函数创造的一个词法作用域,里面创建的变量被引用后,可以在这个词法环境之外自由使用(维基百科)。
闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。闭包的特点:
1. 作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
2. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
简单的说,JavaScript允许使用内部函数—即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
在JavaScript中,闭包通常用来创建函数内部的变量,使这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。
JavaScript中,变量的作用域只有两种:
- 全局变量
- 局部变量
这样就存在一个问题,全局变量可以被所有的函数读写,而局部变量又只能被其所有者(函数)读写。那么如果现在我们需要在外部读写改函数内部时的局部变量时我们该怎么做?
看看下面这个例子:
var add = (function (){
var count = 0;
return function() { return count += 1; }
})();
add();
add();
add();
//count为3
这就是简单的一个闭包的例子,每次调用add都会使count加1。在这个例子中,匿名函数function(){ return count += 1; }可以访问其上层匿名函数所有的局部变量,而下层匿名函数的局部变量对上层又是不可见的。这就是JavaScript特有的“链式作用结构(chain scope)”,子对象可以一层一层地向上访问所有父对象地变量,但子对象地所有变量对父对象不可见。
因此我们把下层匿名函数作为返回值,就可以在匿名函数外部读取并操作它的内部变量。
事实上,就概念而言一个函数本身就是一个闭包,但函数嵌套函数这种类型的闭包作用更大,所以我们通常所用的闭包就是一个函数内嵌套一个返回自身的函数,并提供一个变量(上面的例子是add)作为接口来调用。这样做的直观效果是我们就可以在函数外部修改函数内部的变量了。
更深层次的是,这些可修改的局部变量会一只保存在内存中。下层匿名函数被赋值给一个变量(add),这使得下层匿名函数始终存在于内存中,而它又依赖于上层匿名函数,因此闭包机构所涉及的所有函数都不会在调用结束后被垃圾回收机制(garbage collection)回收。
因此使用闭包需要注意一下两点:
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。
- 不能随便改变上层函数(父函数)内部变量的值。
常见的几种写法:
1、给函数添加一些属性
function Circle(r) {
this.r = r;
}
Circle.PI = 3.14159;
Circle.prototype.area = function() {
return Circle.PI * this.r * this.r;
}
var c = new Circle(1.0);
alert(c.area()); //3.14159
2、声明一个变量,将一个函数当作值赋给变量
var Circle = function() {
var obj = new Object();
obj.PI = 3.14159;
obj.area = function( r ) {
return this.PI * r * r;
}
return obj;
}
var c = new Circle();
alert( c.area( 1.0 ) ); //3.14159
3、这种方法使用较多,也最为方便。var obj = {}就是声明一个空的对象
var Circle={
"PI":3.14159,
"area":function(r){
return this.PI * r * r;
}
};
alert( Circle.area(1.0) );//3.14159