了解闭包之前,应该先了解js的作用域
1. 全局作用域
在代码中任何地方都能访问到的对象拥有全局作用域,所有为定义但直接复制的变量也拥有全局作用域
2.函数作用域(局部)
Js中没有块及作用域,变量在当前函数体或当前函数嵌套的函数中都是有定义的
先看段网上的代码和作者的解释:
name=”lwy”;
function t(){
var name=”tlwy”;
function s(){
var name=”slwy”;
console.log(name);
}
function ss(){
console.log(name);
}
s();
ss();
}
t();当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显
name是”slwy”。
但执行ss()时,作用域链是: ss()->t()->window,所以name是”tlwy”
这里引申出一个优化的问题,因为每次的活动对象都被推到链表的开头,而全局变量链在了最后面,每次调用全局变量都要从新创建并且从链表最上面检索,这就导致了每次使用全局变量,都要从头检索到最后面,大量的这种操作就会使程序效率低下。
1.使用局部变量
当一个函数中重复的使用了两次或两次以上的全局变量,将全局变量定义为局部变量
例如:
var doc = document;
2.推荐一片关于作用域和作用域链的文章
http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html
官方对闭包的定义比较晦涩,在网上找了找有如下几种解释:
闭包就是能够读取其他函数内部变量的函数。
闭包就是一个函数把外部的那些不属于自己的对象也包含(闭合)进来了
定义总结一下:闭包的定义很多,在一个嵌套函数中,函数内部可以引用外部的参数和变量,并且不被GC回收
//外部function
function withoutA(x){
var a = 3;
return function(y){ // 内部返回的function
a++;
alert(x+y+a);
}
}
var insideA = withoutA(2); //被insideA接收
insideA(10); //输出为16
insideA(10); //输出为17
调用withoutA(2)返回的function赋值给insideA
第一次执行insideA(10),x=2,y=10,a++后变为4,输出值为16
第二次执行时因为a还存在于insideA闭包的内部,并且经过第一次执行的++,值为4,执行第二次++后变为a=5,x=2 , y=10,输出值为17
Js里处理object是引用传递,调用widthout时函数return的闭包也会引用之前的object
// without函数return的闭包也会引用最初那个object
function without(x) {
var tmp = 3;
return function (y) {
alert(x + y + tmp);
x.xt= x.xt? x.xt+ 1 : 1;
alert(x.xt);
}
}
var tt= new Number(2);
var insideA= without(tt); // bar 现在是一个引用了tt的闭包
insideA(10); //x.xt= undefind = 1
insideA(10); //x.xt= xt+ 1 = 2 引用了同一个number对象
结束引用,避免大量累加后性能下降
function box(){
var age = 100;
return function(){
age++;
return age;
};
}
var b = box();
alert(b()); //101
alert(b()); //102
alert(box()()); //101
alert(box()()); //101
b = null; //解除引用
需要注意b()与box()(),虽然执行的都是return的function,但box()()并不能累加其中的变量
一个例子:
<html>
<head>
<script type="text/javascript">
function buttonInit(){
for(var i=1;i<4;i++){
var b=document.getElementById("button"+i);
b.addEventListener("click",function(){ alert("Button"+i);},false);
}
}
window.onload=buttonInit;
script>
head>
<body>
<button id="button1">Button1button>
<button id="button2">Button2button>
<button id="button3">Button3button>
body>
html>
可以看到,因为循环后的 i 变成了4,所以不论点哪个都会提示Botton4,用闭包改造一下
<html>
<head>
<script type="text/javascript">
window.onload = function(){
var a = document.getElementsByTagName('button');
for (var i = 0; i < a.length; i++) {
(function (i) {
a[i].addEventListener("click"
,function(){alert("Button" + (i+1))}
,false)
})(i);
}
};
script>
head>
<body>
<button id="button1">Button1button>
<button id="button2">Button2button>
<button id="button3">Button3button>
body>
html>
这样每次调用,i 的值都是当初的 i 值
简单的闭包并不难实现,但了解实现原理并不轻松,闭包的实际应用也要谨慎,如果运用不当很多地方都会出现莫名其妙的错误,大量的累加没有接触引用也会导致性能问题
附上个人觉得写得很好的博客地址:
http://kb.cnblogs.com/page/110782/