闭包和高阶函数
闭包的形成与变量的作用于和变量的生命周期密切相关。
- 变量作用域
var fnc = function () {
var a = 1;
console.log(a); //输出 1
}
fnc();
console.log(a); //输出 Uncaught ReferenceError: a is not defined
在javaScript中, 函数可以用来创造函数作用域,但是只能是函数内部可以访问当前函数外部的变量,而外部的变量是不能访问到函数内部的变量的。这是因为在函数执行时,发现函数内部并没有声明这个变量,那么就会随着代码执行环境创建的作用域链往外层逐层寻找,直到找到为止,变量的搜索是从内到外。
var a = 1;
var fnc1 = function() {
var b = 2;
var fnc2 = function() {
var c = 3;
console.log(a); //输出 1
console.log(b); //输出 2
}
fnc2();
console.log(c); //输出 Uncaught ReferenceError: c is not defined
}
fnc1();
- 变量的生存周期
除了变量的作用域外,另外一个和闭包有关系的概念是变量的生存周期。
对于全局变量,它的生存周期必然是永久的,除非我们主动的去销毁它,而对于函数内部用var 关键字 声明的局部变量来说,当退出函数时,它们都会随着函数的调用结束而被销毁。
var fnc = function () {
var a = 1; //退出函数时后局部变量a将被销毁
console.log(a);
};
fnc();
继续看这一段代码:
var fnc = function () {
var a = 1;
return function() {
a++;
console.log(a);
}
};
var f = fnc();
f(); //输出 2
f(); //输出 3
f(); //输出 4
f(); //输出 5
f(); //输出 6
跟我们之前的推论相反,当退出函数时,局部变量a并没有被销毁,而是在某个地方一直存活着,这是因为执行var f = fnc();时, f返回了一个匿名函数的引用,他可以访问到fnc()被调用时产生的环境,而局部变量a 就处于该环境内部,既然函数内部的变量可以被外界访问到,那么这个变量就有了不被销毁的理由, 由此就产生了闭包结构,局部变量的生命就得到了延续。
下面介绍一个闭包的经典应用:
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
var nodes = document.getElementsByTagName('div');
for(var i = 0; i < nodes.length; i++){
nodes[i].onclick = (function() {
console.log(i);
})
};
</script>
</body>
这样的话,无论点击的是谁,最后的结果都是5,这是因为当前dom绑定的点击事件,是异步触发的。当事件触发的时候,for 循环早就已经执行完毕了,最后一次执行循环完毕加1之后 ,i 一直等于5,。
闭包解决方式:
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
var nodes = document.getElementsByTagName('div');
for(var i = 0; i < nodes.length; i++){
(function(i){
nodes[i].onclick = (function() {
console.log(i);
})
})(i)
};
</script>
</body>
通过使用闭包,把每次循环的i的值都封闭起来,当在事件函数中顺着作用域链中从内向外查找变量i 时,回先找到被封闭在闭包环境中的i,如果有5 个div,这里的i 就分别是0,1,2,3,4。其实也可以理解为,通过函数自调用,把每次循环执行时,把对应的i传入到匿名函数中,其实就是隐性的在函数内部执行了 var i = 传进来的i的值, 当事件触发时,通过作用域链向外搜索时,便能找到对应的i的值了。
喜欢的点个赞,一直持续更新