我们知道,JavaScript中是没有块级作用域的。也就是说,在块语句中定义的变量,实际上是在外部函数中而非在语句中创建的。如下:
function outNumber (count) {
for (var i = 0; i < count; i ++) {
alert(i);
}
alert(i);
}
outNumber(5); //5
以上代码会弹出"0"到"5'的数,实则在for循环中,只会弹出"0"到"4"的数,而"5"这个数字是在外部函数即outNumber中弹出的。变量i的值自增到"5"后,会跳出循环,最后i的值就是"5",并由外部函数弹出这个值。
在C、JAVA或类C的语言中,变量i只会在for循环中有定义,一旦循环语句结束,变量i也会被销毁。但是在JavaScript中,变量i是定义在外部函数outNumber()的活动对象中的,因此从它被定义开始,就可以在函数体内部随处访问它了,即使像下面重新声明该变量一样。
function outNumber (count) {
for (var i = 0; i < count; i ++) {
alert(i);
}
var i; //重新声明此变量
alert(i); //弹出变量i最后的值
}
outNumber(5); //5
即使这样,也会像上例一样弹出结果。像这样重新声明变量,它只会忽略最后的声明。
注意:这只是重新声明了这个变量,并未赋值。如果重新声明并赋值的话,就会有不同的结果了。
function outNumber (count) {
for (var i = 0; i < count; i ++) {
alert(i);
}
var i = 6;
alert(i);
}
outNumber(5); //6
像这样,for循环中会弹出"0"到"4"的数。而outNumber函数中会弹出"6"数字,因为此时重新定义和初始化了变量i,相当于"var i = 6"覆盖了"var i =5",因此,最后弹出的是"6"。
举例上述一些例子只是说明:块语句中定义的变量,不仅可以在块语句中访问,也可以在外部函数(包含函数)访问到,而在实际开发中,我们不希望在包含函数中能访问该变量。因为解决的方法就是:
使用块级作用域(私有作用域)的匿名函数来表示。
(functin () {
//这里表示块级作用域;
})();
以上代码定义并立即调用了一个匿名函数。将函数定义在一个圆括号里,实际上它就是一个函数表达式。后面紧跟着的圆括号,表示调用它。
我们知道,声明一个函数有两种方法:
1、使用关键字function后跟构造器的方法创建一个函数。
function func (a, b, c...) {
//这里是函数体
}
func();//调用这个函数
2、使用函数表达式的方式创建函数:将一个匿名函数赋值给一个变量。
var func = function (a, b, c...) {
//这里是块级作用域
};
func();//调用这个函数
这里第二种方法创建的函数,是先创建一个匿名函数,并把这个匿名函数赋值给一个变量,调用函数的方式就是这个变量名后跟一个圆括号。这里,"func()"的这种方式我们是否可以看作是:
function (a, b, c...) {
//这里是块级作用域
}();
但这样是会出错的,因为JavaScript将function关键字当作一个函数声明的开始,而函数声明后面是不能带圆括号的。
我们知道一个函数表达式后面是可以跟圆括号的,就是像"func();"这样,那么将函数声明转换成函数表达式,就是在匿名函数上加上圆括号:
(function (a, b, c...) {
//这里是块级作用域
})();
无论在任何地方,只要是临时需要一个变量,就可以使用私有作用域,这样,变个变量就不会影响全局作用域了。
function outNumber (count) {
(function () { //临时需要一个变量i创建for循环
for (var i = 0; i < count; i ++) {
alert(i);
}
})();
alert(i); //出错,这里的变量i未声明。
}
outNumber(5);
如上,在for循环外部插入一个匿名函数。在匿名函数中定义的变量,都会在匿名函数执行结束后被销毁。因此,变量i只能在for循环中使用,使用后被销毁。在私有作用域中能访问到参数count,是因为匿名函数是一个闭包,闭包能访问包含函数作用域中的所有变量。
私有作用域这种技术通常是在全局作用域上的函数外部使用,从而限制向全局作用域中过多的添加全局变量和函数。在实际开发中,我们应当尽量减少向全局作用域下添加过多的全局变量和函数,不然会形成内存泄漏的。
在实际开发中,很多时候一个项目是由多个开发人员完成的,每个开发人员做的程序实现的功能不同,那么可能会需要多个变量,过多的全局变量和函数会导致命名冲突等问题。如果此时使用私有作用域,每个开发人员不仅有自己的变量,也不用担心搞乱全局作用域了。
(function () {
var nTime = new Date();
if (nTim.getMonth == 0 && nTime.getDate == 1) {
alert("Happy new Year!!!");
}
})();
以上代码功能就是判断当前时间是否是1月1日,如果是就输出祝福语。这个功能只是一个程序中小功能之一,函数调用执行后,局部变量nTime就会被销毁,腾出所占用的内存空间。当一个程序有多个小功能时,每个功能都会有自己的局部变量,执行完成后又被解除、释放其所占用空间,避免了内存泄漏。
通过以上的例子说明:使用私有作用域的技术可以减少闭包所占用的空间,因为没有指向匿名函数的引用。当函数自调用执行完成后,马上销毁其作用域链了。还可以限制了向全局作用域中添加全局变量和函数,减少内存的占用率。