js中的函数闭包是一个比较重要的概念,那么什么叫函数闭包呢?
函数闭包指的是每一个函数在调用时都有一个自己的词法环境,这个词法环境是封闭的,外部无法访问,因此就叫做闭包。先看几个例子。
function a(){
var content = 1;
return ()=>{console.log(content)};
}
a()();
函数a的词法环境中有一个变量content它的值为1。当函数a被调用时,它会返回一个打印函数,再调用这个打印函数就会打印出content的值。为什么这个打印函数能访问到content的值呢?因为打印函数在函数a的内部,它能访问到函数a的词法环境。而在函数a外部,我们无法访问到content变量。
我们再来看一个例子
var a = [];
for (var i = 0; i < 10; ++i)
a[i] = function () { console.log(i); }
a[0]();
a[5]();
a[9]();
上面这三个函数调用会打印出什么值呢?或许你会脱口而出0,5,9。但是事实上三个函数都会打印出10。这是为什么呢?有了上面的分析也许我们会明白。在for循环中声明的这个十个函数都共享同一个词法环境也就是说这十个函数中的i都是同一个。那么你再来看看,当for循环执行完毕后,i的值变为10。然后当你调用函数时,访问i变量,那它的值当然是10了,而且不管你调用这十个函数中的哪一个,都会打印出10。那么我们怎么样才能实现让它打印出我们想要的0,5,9呢?再看下面一段代码。
var a = [];
function print (content){
return function(){
console.log(content);
}
}
for (var i = 0; i < 10; ++i)
a[i] = print(i);
a[0]();
a[5]();
a[9]();
我们给a[i]赋值时直接调用函数print。每一个a[i]都会调用一个print函数。这个print函数是接受一个content参数作为自己的词法环境。而这个环境是封闭的,也就是说你每调用一次print函数都会有一个封闭的词法环境,你每次传递的参数值都不同,这样是不是就实现了我们想要打印出的结果。
还有一种函数自调用的方法与此类似,请看下面代码。
var a = [];
for (var i = 0; i < 10; ++i)
a[i] = (function(){
var value = i;
return function(){console.log(value)};
})();
a[0]();
a[5]();
a[9]();
同样我们给a[i]赋值时就先调用一个函数,这里是让这个函数自调用。当它每次自调用时都会有一个封闭的词法环境。在这些词法环境中变量value保存了每次i的值,这样你再返回一个打印value值的函数是不是也能得到0,5,9的结果了。
其实还有一个更简单的方法就是使用es6中的关键字let。声明i为块变量,这样每次循环都是一个新的块,每个块的i值都不一样这样就能轻易地实现我们想要的效果。这里只是为了帮助我们理解闭包而举的一个简单例子。