函数内部可以直接读取全局变量,函数 f1 可以读取全局变量 n。
但是,在函数外部无法读取函数内部声明的变量
[JavaScript] 纯文本查看 复制代码
function f1() {
var n = 99;
}
f1()
console.log(n);
有时我们却需要在函数外部访问函数内部的变量;正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
[JavaScript] 纯文本查看 复制代码
function f1() {
var n = 999;
var f2 = function() {
console.log(n);
}
return f2;
}
var f = f1();
f();
上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。
这就是JavaScript语言特有的”链式作用域”结构(chain scope),子级会一层一层地向上寻找所有父级的变量。所以,父级的所有变量,对子级都是可见的,反之则不成立。
既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。
闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
【闭包的作用】
1. 匿名自执行函数
我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,
比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。
除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,
比如UI的初始化,那么我们可以使用闭包:
[JavaScript] 纯文本查看 复制代码
var data= {
table : [],
tree : {}
};
(function(dm){
for(var i = 0; i <dm.table.rows; i++){
var row =dm.table.rows;
for(var j = 0; j< row.cells; i++){
drawCell(i,j);
}
}
})(data);
我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在函数执行完后会立刻释放资源,关键是不污染全局对象。
2. 结果缓存
我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,
那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
[JavaScript] 纯文本查看 复制代码
var CachedSearchBox = (function(){
var cache = {},
count = [];
return {
attachSearchBox :function(dsid){
if(dsid incache){//如果结果在缓存中
returncache[dsid];//直接返回缓存中的对象
}
var fsb = newuikit.webctrl.SearchBox(dsid);//新建
cache[dsid] =fsb;//更新缓存
if(count.length> 100){//保正缓存的大小<=100
deletecache[count.shift()];
}
returnfsb;
},
clearSearchBox :function(dsid){
if(dsid incache){
cache[dsid].clearSelection();
}
}
};
})();
CachedSearchBox.attachSearchBox("input");
这样我们在第二次调用的时候,就会从缓存中读取到该对象。
3. 封装
[JavaScript] 纯文本查看 复制代码
var person = function(){
//变量作用域为函数内部,外部无法访问
var name ="default";
return {
getName :function(){
returnname;
},
setName :function(newName){
name =newName;
}
}
}();
print(person.name);//直接访问,结果为undefined
print(person.getName());
person.setName("abruzzi");
print(person.getName());
得到结果如下:
undefined
default
abruzzi