闭包是jser绕不过的坎,一直在都在说,套用 simpson 的话来讲:JavaScript中闭包无处不在,你只须要可以识别并拥抱它。前端
闭包是基于词法做用域书写代码时的天然结果,你甚至不须要为了利用它们而有意识的去建立闭包。闭包的建立和使用在你的代码中随处可见。你缺乏的只是根据你的意愿来识别、拥抱和影响闭包的思惟环境数组
当函数能够记住并访问所在的词法做用域时,就产生了闭包。即便函数是在当前词法做用域以外执行 --《你不知道的js》(上卷)
闭包是指有权访问另外一个函数做用域中的变量的函数 --《JavaScript高级程序设计》
先来看一个例子:
例子1:闭包
function foo(){ var a = 2; function bar(){ console.log(a); // 2 } bar(); } foo()
这是闭包吗?
这个代码从技术上来讲是,但也能够说不是。准确的来讲bar()对a的引用的方法是词法做用域的查找规则。咱们再来看:
例子2:函数
function foo(){ var a = 2; function bar(){ console.log(a) } return bar; } var baz = foo(); baz(); // 2, 这就是闭包了
在例2中,咱们将bar()函数自己当作一个值类型进行传递,函数bar()可以访问foo()的内部做用域。在这个例子中,它在本身定义的词法做用域之外的地方执行。spa
要了解清楚,得先了解几个概念设计
每一个函数都有本身的执行环境。这个环境能够访问外部环境,以此类推。每一个环境能访问到的标识符集合,称之为 做用域,也就是词法做用域。code
将做用域一层一层的嵌套,就造成了做用域链对象
以下,一般咱们都但愿foo()
在执行完成之后,整个的内部做用域都被销毁。由于咱们知道引擎有垃圾回收机制用来释放再也不使用的内存空间。因为看上去foo()
的内容不会再被使用,因此很天然的想到会对其回收。可是,事实上内部做用域依然存在blog
var globalVar = 10; function foo() { var fooVar = 20; function bar() { var barVar = 30; return globalVar + fooVar + barVar; } return bar; } var baz = foo(); baz();
如上,用一张图表示索引
这个做用域链在函数建立的时候就保存起来了。
baz()
函数在执行的时候(执行bar()
函数),将当前的变量对象(因为当前的环境是函数,因此将其活动对象做为变量对象)添加到做用域链的前端。此时,因为bar()
在执行,而做用域链也存在,因此能够在做用域链上进行查找,去访问foo()
的变量。
闭包经典问题
// 函数做用:但愿它返回一个数组。该数组的元素为遍历的索引值 function hello(){ var res = []; for (var i = 0,len = 5;i < len;i++){ res[i] = function () { return i; } } return res; }
返回的结果跟咱们期待的不同,由于:闭包保存的是整个变量对象,而不是每一个变量。
解决方案:
function hello(){ var res = []; for (var i = 0,len = 5;i < len;i++){ res[i] = (function(i){ return i; })(i) } return res; }
这里,没有没有把闭包直接赋值给数组。而是定义了一个匿名函数,而且将当即执行该匿名函数的结果赋值给数组,因为参数是按值传递的,因此会将当前值传给参数num。
参考资料:《你不知道的js》(中卷)、《JavaScript高级程序设计》
完