js中的闭包
闭包是学习js中永远也绕不过去的一个坎,那么,今天我们就去一段简单的代码开始聊一聊闭包
什么是闭包
这个概念性的东西翻译有很多种,纯官方的翻译比较晦涩难懂,我们把它理解成一个比较特殊的函数就行。按照网上的说法来说就是:
「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
function close(){
varn=999;
var getNumber=function(){
return n;
};
return getNumber;
}
在close函数里面有一个getNumber方法,通过getNumber方法可以访问到函数的内部变量。
闭包的特性
能够读取函数内部的变量
能让这些变量的值始终保持在内存中
第一点很好理解,我们平时可能在不知不觉中就使用了闭包的这个特性,而第二点虽然用的不多,但是在面试中经常遇到。首先我们先看一段代码:
function f1(){
varn=999;
nAdd=function(){
n++;
};
function f2(){
console.log(n);
}
return f2;
}
var result=f1();
result();//999
nAdd();
result();//1000
为什么第二次执行后打印的结果是1000,而不是999,因为n被保存下来了,并没有被内存回收机制回收。
为什么每有被回收?因为f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
面试题解读
这是一个很常见的闭包面试题:
varresult=[ ];
function foo(){
vari=0;for(;i<3;i=i+1){
result[i]=function(){
alert(i);
}
}
}
foo();
result[0]( );// 3
result[1]( );// 3
result[2]( );// 3
运行结果为什么是3,我简单的重现一下代码运行的过程,
i=0;result[0]=function(){alert(i)};
i=1;result[1]=function(){alert(i)};
i=2;result[2]=function(){alert(i)};
运行的时候在function内部放的是一个变量i,只有被执行的时候才会给这个i赋值。当执行result[0]的时候,里面的变量i因为在当前作用域下并没有被定义,所以向它的父级去找,此时for循环已经执行完毕,i的值是3,所以弹出的都是3。为什么i保存下来了,因为result函数依赖于foo函数,所以foo一直在内存中,i变量也没有被内存回收机制回收。
那么如何实现弹出的是0,1,2呢?
利用之前提到的闭包就可以了,代码如下:
varresult=[ ];function foo(){
var i=0;
for(;i<3;i=i+1){
(function(){
var index=i;
result[i]=function(){
alert(index);
}
})()
}
}
foo();
result[0]();// 0
result[1]();// 1
result[2]();// 2
利用立即执行函数(IIF),我们可以创建一个闭包,在这个IIF内部,我们使用index将i给保存下来了。具体执行过程如下:
i=0;
(function(){
var index=0;
result[0]=function(){
alert(index);
}})()
i=1;
(function(){
var index=1;
result[1]=function(){
alert(index);
}})()
i=2;
(function(){
varindex=2;
result[2]=function(){
alert(index);
}})()
因为这些语句都放在IIF中,所以都有各自的作用域,index并不会重复。就像下方的代码:
var sayHi=function(){
var words="hi"
}
var sayHello=function(){
var words="hello"
}
两个方法中虽然都有words这个变量,但是因为在不同的函数中,都有各自的作用域,所以互不干扰。
参考文献