封闭空间的主要思想在于:
- JS中给一个变量外面加小括号,是不改变任何结果的。比如
var show = function(){ //定义一个名字为show的函数
alert(12);
};
show(); //调用名字为show的函数
上面的show()
也可以写成(show)()
,然后因为show
本身又代表function(){alert(12);};
,所以左边括号里的show
完全可以用函数本身代替而写成(function(){alert(12);})()
。而右边的括号里可以传参数。
这样做的好处至少有:
- 可以在定义函数的同时又可以执行它,省去命名的步骤;
- 封闭空间里定义的变量是这个空间里的局部变量。
看下面这个典型错误的选项卡例子,是运用封闭空间来解决for循环里事件函数里i的问题
for (var i=0; i<aBtn.length; i++) {
aBtn[i].onclick = function(){
for (var i=0; i<aBtn.length; i++) {
aBtn[i].className = '';
aDiv[i].style.display = 'none';
}
this.className = 'active';
aDiv[i].style.display = 'block'; //这行的i就是有问题的,因为在没有点击aBtn[i]之前而执行里面代码的时候i早就变成了3,而不是我们要的每次循环得到的那个i
};
}
上述代码是错的,可以想到,如果for循环里的代码如果是一个函数执行,那函数执行就能和for循环同步执行,每次循环i,只要把i传进里面函数的参数里,就能保证同时递增(严格来讲也不是同步,这个以后再说)。所以完全可以用封闭空间的方式来直接执行函数,并把i当做参数传进封闭空间函数即可。
for (var i=0; i<aBtn.length; i++) {
(function(index){ //在定义的时候就要求传一个index变量
aBtn[i].onclick = function(){
for (var i=0; i<aBtn.length; i++) {
aBtn[i].className = '';
aDiv[i].style.display = 'none';
}
this.className = 'active';
aDiv[index].style.display = 'block'; //这里的index就相当于每次递增的i
};
})(i); //这里将每次递增的i传给封闭函数里的参数
}
上面的例子,最外层for循环里的代码是一个封闭空间函数,它在定义的同时就执行了,而且是通过传参的方式将每次递增的i传进了函数,所以可以每次执行i都不同。
- 封闭空间的注意事项
一般在工作中,用封闭空间可以很好地避免变量重名的问题,比如下面这个例子
//下面是员工A写的代码
var oBtn = document.getElementsByTagName('input')[0];
oBtn.onclick = function(){
alert(oBtn.value);
};
//下面是员工B写的代码,但是他们2个人是分别在不同的文件里写的,而他也定义了oBtn,所以把前面的覆盖了
var oBtn = document.getElementsByTagName('input')[1];
oBtn.onclick = function(){
alert(oBtn.value);
};
为了避免这样的情况出现,可以用封闭空间的方法,把各自的变量封闭为局部变量
//下面是员工A写的代码
;(function(){
var oBtn = document.getElementsByTagName('input')[0];
oBtn.onclick = function(){
alert(oBtn.value);
};
})();
//下面是员工B写的代码
;(function(){
var oBtn = document.getElementsByTagName('input')[1];
oBtn.onclick = function(){
alert(oBtn.value);
};
})();
这时每个函数里的oBtn只在各自的函数里起作用。
另外每个(function()
前加一个;
的目的是,在实际工作中,不同人写不同的部分,最后会压缩在一个文件中,而这个;是为了防止前面的一个人写的代码忘了用;结束,而导致后面的代码出错,但是即使2个;;连在一起也不会有问题。