理解JavaScript闭包(closure)
闭包听了很多次了,可是到底有那些具体的用法还是不清楚,看了《JavaScript高级程序设计》,有点明白了。
1.闭包的定义:
闭包其实就是一个函数,而这个函数有点特别,它能够访问另一个函数作用域中的变量,所以一般我们看到的闭包存在形式都是在一个函数里面。
示例:
var iBaseNum = 10;//全局变量
function addNum(iNum1, iNum2) {//局部变量
function doAdd() {//闭包函数,有权访问局部变量中的iNum1, iNum2参数以及全局变量iBaseNum
return iNum1 + iNum2 + iBaseNum;
}
return doAdd();
}
2.闭包的用处
我们知道,当在函数内部定义了函数时,自然而然也就创建了闭包,闭包之所以强大,是因为它内部存在着一条作用域链。这条作用域包含着自己的作用域,包含函数的作用域和全局作用域。当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。
通常,当一个函数执行完了,它的作用域以及其所有变量都回在函数执行结束后被销毁。,但是当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存,直到闭包不存在为止。
了解了闭包的特性,我们就可以更好理解闭包有那些用途了,因为用途的实现是依赖于闭包的特性。
(1)模仿块级作用域
function outputNumbers(count){//没有闭包
for (var i=0; i < count; i++){
alert(i);
}
alert(i); //count
}
outputNumbers(5);
输出结果:0,1,2,3,4,5是alert(i)输出的。
for执行完了,i 还是存在于函数作用域中。
function outputNumbers(count){
(function () {//闭包函数,且立即执行
for (var i=0; i < count; i++){
alert(i);
}
})();
alert(i); //ReferenceError: i is not defined,出现错误了
outputNumbers(5);
输出结果:0,1,2,3,4
由于for在一个闭包函数里,当闭包函数执行完了,i也就销毁了,所以再在外部函数作用域在使用它就会出现未定义的情况了。
(2)创建私有变量
利用私有变量特性,我们就可以隐藏那些不应该被直接修改的数据了
私有变量定义:任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。私有变量包括函数的参数,局部变量和在函数内部定义的其他函数。
示例:
function add(num1,num2){
var sum=num1+num2;
return sum;
}//num1,num2,sum都是私有变量
私有作用域(块级作用域):只在内部可以使用,外部无法访问
示例:
//创建私有作用域
(function(){
//这里是私有作用域
})();//正确
//错误方法
function(){
//这里是私有作用域
}();//出错
JavaScript将function关键字当作一个函数申明的开始,而函数申明背后不能更圆括号,然而,函数表达式的后面可以跟圆括号,要将函数申明转换成函数表达式,只要将其放在一对圆括号中即可。
特权方法:我们把有权访问私有变量和私有函数的公有方法叫做特权方法。
示例:
function Person(name){//name是私有变量
this.getName = function(){//特权方法
return name;
};
this.setName = function (value) {
name = value;
};
}
var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"
上面示例中的getName()和setName()都可以在外部使用,都有权访问私有变量name。除了使用构造函数内部定义的这两个方法外,外部没有其他的任何办法可以访问name。
可以使用构造函数模式,原型模式来实现自定义类型的特权方法,也可以使用模块方式,增强的模块方式来实现单例的特权方法。