写在前面:本文案例除特别指明,默认基于chrome mac版。你可能想:闭包么难道还有浏览器差异么?!嘿嘿,你且看来:
按我的理解,闭包就是可以在函数外面访问其内部变量的一种方式。因为大家都知道,javascript执行时遵循一个隐式的作用域链,查询变量的值时将沿作用域链一路向上查找。
先看看常规方法和闭包的不同:
var i='global';
var func=function(){
console.log(i);
}
func();
此时的输出为'global'。
这就是常规作用域链的最简单的例子,当我们在执行func()方法时,因为其内部并没有定义i,所以它开始向上查找i值,最终在全局环境中找到i.
var i='global';
function constfuncs(){
var i=10;
var func=function(){
console.log(i);
}
return func;
}
var f=constfuncs();
f();
此时的输出为10。
此例中f函数和上例中的func函数一样,在执行时都没有在自身的上下文中找到i值。但不同的是,func函数向上一级查找是到全局环境,而此例中f方法却找到了constfuncs中。啊哈,这就是今天讲的闭包。闭包可以访问到另一个函数的局部变量呢~
在js中,如果函数执行完,正常情况下,该函数的局部变量将被销毁,外界并不会访问到该函数中的局部变量,比如:
function a(){
var i=10;
console.log(i);//10
}
console.log(i);//undefined
但是本例中的constfuncs因为返回了一个函数变量,且该函数变量中又引用了constfuncs函数中的i,所以此时constfuncs在执行结束后,其中的变量并不会被销毁,而是要为它返回的函数"留着"。
且看下面的截图:
当f函数执行到console.log(i)时,它对应的上下文在调试器中非常清楚:Local指该函数的局部变量,Closure指的就是闭包变量,而Global为全局变量。此时找i值的顺序就是Local || Closure || Global
如果i值不是一个恒定的值,而是在外层函数执行中进行了一系列的变化,那么当调用返回的函数时,又执行了什么操作呢?
function constfuncs() {
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] = function () {
return i;
}
}
return funcs;
}
var funcs = constfuncs();
alert(funcs[1]());
此时的输出为10.
其实仔细来推敲一下,当向funcs数组添加项时,每项都是function(){console.log(i)},这里i值只是简单指向外层的i值。当funcs1执行时,用到i值了,它开始去找闭包变量中的i,而此时i值已经固定,为10。所以,不管执行funcs中的哪一项,它用到i值时,都只能得到最终的i值。
打断点看清函数在执行时的上下文:Closure中的i值为10。
那么如何才能使funcs中的每项执行的时候,能用到"被定义时的真实环境"信息呢?也就是,"切断"与初始值i的一些联系,因为i最后又只是一个最终值....看下面 这样行不行:
function constfuncs() {
var funcs = [];
for (var i = 0; i < 10; i++) {
var v = i;
funcs[v] = function () {
return v;
};
}
return funcs;
}
var funcs = constfuncs();
console.log(funcs[1]());
我没得到一个i值就把它存成v,然后使用v来给funcs赋值,最后我再执行funcs中的函数时,不就不去访问i了嘛?就是访问v了,而每次都新建一个v呢~如果外层函数constfuncs每次都因为闭包的原因把v存起来,我最后不是会有很多个v嘛?我好聪明耶!我都脑洞大开想了一通了,来实践一下:
结果....结果很让我失望,输出了9.....囧
为啥呢?
回头又仔细推敲下我刚才说的,存很多个v??
人家为啥要给你存很多个 v啊?当constfuncs函数结束执行后,它会把闭包中要用到的变量存起来没错,但是人家不能记着执行过程都给你存着。注意:我之所以做这个实验是因为v和i我认为是不同的,v是每次都新建一个,而i值是重复改写。浏览器说,你再来感受下:
function constfuncs() {
var i=10,j=30;
var func=function(){
return i;
}
return func;
}
var f = constfuncs();
console.log(f());
这里又展现出了浏览器的不妥协:我不光给你存个最终值,我连你用不到的值也不存。额...你好机智..
等等,我们看看其他浏览器的处理,safari是这样的:
firefox和chrome是一样的处理方式,即能少则少:
说了这么多,我们还没达到目的呢,刚才那个方法还是被i牵着鼻子走呢?再来!
function construct(v) {
return function(){
return v;
}
}
function constfuncs() {
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] = construct(i);
}
return funcs;
}
var funcs = constfuncs();
console.log(funcs[4]());
哇哈!输出4!
这里的关键在于,我把i作为参数传给construct函数声称了一个新函数并赋予funcs中的每一项,而construct返回的每个函数都是有其单独的闭包环境的,就是construct构造的闭包环境啦。且看:
所以,当闭包形成时,所有被用到的局部变量都被保存,参数也是如此。
总结
* 闭包,可以说是一个封闭的环境,对外给出的函数就像一个口子,打开闭包外访问内部的一个门。
* 当闭包会保存外部可能访问到的其内部的一切值,当然包含其外层函数的参数。
* 根据浏览器的不同,当产生闭包时,值的保留有些会全部保留(如safari),有些会保留用到的值(如chrome 和 firefox) 。
感谢看到最后~本人不才,还望能助你理解一二。如有不同见解,还请留言。