如果你是前端程序员,你可以问自己。克洛泽到底是什么东西?肺包我真的知道吗?闭包是前台笔试面试过程中必然会遇到的问题,所以有必要明确闭包。

一些概念性和基础性的问题,大家可以在网上查的到,这里我们从具体的题目入手,快速提升对于闭包的理解。

理解javascript闭包

我们直接看几道在面试中经典的闭包问题

  • ul中有若干个li,每次点击li,输出li的索引值

这道题在闭包中应该属于最经典的问题,一般人都会很快写出如下的代码:

错误的解法

但是真正运行后却发现,结果并不如自己所想,每次log出来的并不是索引值,而是表示li个数的值。那么这是为什么呢?因为,在我们点击li,触发li的click事件之前,for循环已经执行结束了,此时内存中的i只有一个值,那就是i=lis.length,所以每次点击li后返回的都是lis.length的值。采取闭包的方法可以很好的解决这个问题

正确的解法

上述方法,由于闭包的存在,每个索引i的值将被拷贝一份放在闭包中,在函数调用时就可以直接访问到i的值,按照正确的索引输出。

  • 定时器问题

直接来看一段代码

第一眼看上去,你肯定能想到这是考的闭包问题,于是就会说1秒,2秒,3秒的时候各输出一次three。但是当你在机器上运行后却发现,0秒,1秒,2秒各输出一次undefined。

纳尼?undefined?如果你仔细想一下就会明白,在setTimeout的函数执行时,for循环已经结束,此时i=4,而arr[4]=undefined,所以三次都是返回undefined。而函数的执行过程是在i=0,i=1,i=2时才能执行定时函数,所以是在0秒,1秒,2秒的时候输出undefined。可以采取以下使用闭包的方法解决问题

定时器问题正确解法

  • 作用域链

在闭包中常常会涉及到作用域链的问题,看下面一段代码

作用域链问题

通过代码可以看出object.method()返回的是一个匿名闭包函数,而在该匿名函数中返回的是,在javascript中this指向的永远是函数调用的实体,此时this指向的是最外层的对象,在最外层定义了name="outer",因此结果输出是"outer"

再来看一下代码段1的变种代码段2

作用域链问题

在代码段2中,首先将this赋给了一个成员变量that,因此,函数调用的作用域就限制在了obj对象中,其他的如前面代码段1的分析,最后结果输出的是"inner"

  • 多个相同函数名

多个相同的变量名称问题

在上述代码段中,我们首先要分析下每个foo指向的是什么。

首先最外层的foo是一个具名函数,返回的是一个具体的Object对象;

第二个foo属性,它是最外层foo函数返回对象的一个属性,该属性指向一个匿名函数;

第三个foo是一个被返回的函数,根据作用域链的理解,foo()在最外层环境中定义,而在foo()的局部环境中未定义,所以第三个foo实际是和第一个foo指向同一个函数。

理清三个foo的指向后,我们再来看看具体的执行过程:

(1)foo(0)的时候,未传递b值,所以返回undefined;

在x.foo(0)时,foo闭包了外层的a值,就是第一次调用的0,此时c=1,因为第三层和第一层为同一个函数,实际调用为第一层的的foo(1, 0),因此x.foo(1)会输出0。

同理理解x.foo(2)和x.foo(3)都是输出0

第一行结果为undefined,0,0,0

(2)foo(0)时,b为传递值,o为undefined;

在链式调用foo(1)时同(1)中 的分析,最后调用为foo(1, 0),输出0

链式调用foo(2)时,仍然调用的是第二个foo,此时c=2,而由于闭包的存在foo闭包了第二次的变量a,因此a=1,实际调用为foo(2, 1),因此输出1;同理foo(3)的时候会输出2

第二行结果为undefined,0,1,2

(3)前两个执行结果foo(0).foo(1)同(1)(2)中分析是一样的,foo(2)时,c=2,foo闭包了第二次的变量a,此时a=1,因此相当于foo(2, 1),输出1

z.foo(3)同理z.foo(2),也返回1

第三行结果为undefined,0,1,1

总结

如果你认真看完了这篇文章,应该会对闭包的理解有一定的提升,当然闭包的知识还远远不止这些,需要平时查漏补缺。

如果喜欢的话,记得关注小编噢,小编后续会坚持出更多技术性的文章,如果有任何问题,也欢迎提问,小编都会尽力解答的。

相关推荐