js中的闭包是一个比较常见的核心概念,之前我们讲的重载,函数柯里化中都用到了闭包,那么到底什么才是闭包呢,闭包能为我们干嘛呢?
按照js犀牛书的定义,闭包是指函数变量可以保存在函数作用域内,这句话的意思貌似太笼统了,而且范围很广,一般的函数内都会包裹变量,那么也可以说有变量的函数就叫闭包吗?显然不是,我们看下js高程里的定义
闭包是指有权访问另一个函数作用域中的变量的函数,这就是说访问外层函数变量的内层函数就叫作闭包咯。那么我们换一种意思理解,闭包是指那些在当前作用域上的函数或者变量被下层作用域所调用,那么我们先来看一个简单的闭包
function a(){ var c= 2; var b = function(){ console.log(c); } b(); } a(); //输出2
在上面的函数中,我们声明定义了一个a()函数,在函数里有个变量c ,该变量被内层函数b()使用输出,最后调用内层函数b();那按照上面的定义,因为在b的作用域内,调用了属于a作用域内的变量,所以形成了闭包。我们通过分析执行环境来看一下闭包的过程
function foo(){ var a = 2; function bar(){ console.log(a); } test(bar); } function test(fn){ fn(); }
foo();
首先,进入全局执行环境,foo()和test()进行变量声明,然后进入第十行执行foo()函数,此时进入foo的执行环境
首先a 和bar()先进行声明,然后执行第六行代码tes(bar) ,此时进入作用域链进行查询,查询test(),在全局作用域中找到test,然后进人test执行环境,然后执行bar,向上foo中查找bar,找到bar,然后执行console,此时console是全局自带的,在全局执行环境中找到,然后查找a,在foo执行环境中找到a,最后输出2;这样一来,我们就能很清楚的弄清楚闭包了,在局部函数中,变量通常会在函数执行后就会消失,但因为有闭包存在,闭包变量在别的作用域中被调用,所以a不会消失,成为自由变量,直到被再次找到,闭包会造成内存泄漏也是此原因。
最后我们留一道题目给大家思考,看一下对闭包的理解是否到位
for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log(i); }, i*1000 ); }
上面的代码会输出5个6,那么如何才能输出1,2,3,4,5呢? 这里提示一下,这里的for循环在执行时会生成5个定时器,然后定时器在沿着作用域链上去查找i,而此时的i的值可能跟你预想不一样,这里需要用到闭包来做。
下回给大家讲下作用域。