大白话理解闭包及相关笔试题
我们先来一个最简单的函数(如下)。
function fn(){
var num = 5;
num+=1;
alert(num);
}
fn(); //6 第一次调用是6
fn(); //6 第二次调用还是6
理由: 函数一旦调用里面的内容就会被销毁,下一次调用又是一个新的函数。
第一次调用后打印出6,第二次调用又相当于重新进行第一次调用,依旧是6。
这个时候,就需要我们的闭包来出面解决,那么我们先来了解下什么是闭包吧。
闭包可以解决函数外部无法访问函数内部变量的问题
通过闭包我们可以让函数中的变量持久保持。来看
function foo(c){
var num = c;
return function A(){
num++;
return num;
}
}
var b = foo(5); //b = function A
b();//6
b();//7
我们执行的是函数b(从foo返回出来的),每次调用函数b,并没有重新执行foo,所以也就不会每次给num重新赋值5。
至于为什么会出现累加呢,这是因为函数foo执行完后,其内部的的A函数里面对num有引用,所以foo的作用域以及变量num被保留在了函数A中,返回给了b。
现在,我们就能在外界通过函数b来访问foo内部的变量num了。
这就是闭包,我们通过返回一个函数打通了函数内部与外界的桥梁。
我们首页定义了一个fn函数,里面有个num默认为0,接着返回了一个匿名函数(也就是没有名字的函数)。我们在外部用f接收这个返回的函数。这个匿名函数干的事情就是把num加1,还有我们用来调试的alert。
这里之所以执行玩这个函数num没有被销毁是因为那个匿名函数的问题,因为这个匿名函数用到了这个num,所以没有被销毁,一直保持在内存中,因此我们f()时num可以一直加
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());
object.getNameFunc()
返回了一个匿名函数:
function(){
return this.name;
};
this对象是在运行时基于函数的执行环境绑定的,
匿名函数的执行环境具有全局性,
因此匿名函数的this指向window
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());
应用场景:
循环绑定的问题
<ul class="list"> <li class="bloc">1</li> <li class="bloc">2</li> <li class="bloc">3</li> <li class="bloc">4</li> <li class="bloc">5</li> </ul> var ali = document.querySelectorAll('ul li') for(var i = 0,l = ali.length;i < l;i++){ ali[i].onclick = function(){ console.log(i) //5 5 5 5 5 } }
reason:
我们绑定了五次onclik事件,无论你有没有触发后面的的函数,for循环都会执行。
当你点击的时候,for循环已经执行完了,i 的值早已经变成了5。var 声明的i能够穿透作用域,每次触发点击事件时,函数内部的 i 也都是5。
那么,我们应该想办法把每次循环时的 i 值保存下来呢。
于是,通过闭包可以保存每次循环的 i 值,请看例子:
for(var i = 0,l = ali.length;i < l;i++){ ali[i].onclick = (function(j){ return function(){ console.log(j)
} })(i) }
for(var i = 0,l = ali.length;i < l;i++){ (function(j){ ali[j].onclick = function(){ console.log(j) //5 5 5 5 5 } })(i) }
通过立即执行函数后返回了一个函数的形式,把每次循环的 i 值通过传参放到了不同的函数中,
每个函数都是一个独立的作用域,每个函数都有了各自的i值,互相不影响,现在就如我们期望的一样了,点击第几个li打印出第几个 li 的下标。