闭包原理以及使用场景

2020/11/25 20:24:34

本文主要是介绍闭包原理以及使用场景,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前端面试,必问闭包

闭包有多重要?如果你是初入前端的朋友,我没有办法直观的告诉你闭包在实际开发中的无处不在,但是我可以告诉你,前端面试,必问闭包。面试官们常常用对闭包的了解程度来判定面试者的基础水平,保守估计,10个前端面试者,至少5个都死在闭包上。

讲解闭包时,我们先来看个例子

http://img1.sycdn.imooc.com/5fbb95a00001c01209280154.jpg

上面代码中,函数f1可以读取全局变量n。但是下面例子,函数外部无法读取函数内部声明的变量。

http://img3.sycdn.imooc.com/5fbb95e50001cdd809280154.jpg

上面代码中,函数f1内部声明的变量n,函数外是无法读取的.

如果有时需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

http://img4.sycdn.imooc.com/5fbb95e500014e6b09310178.jpg

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!


闭包是什么

闭包是一种特殊的对象。

它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。

当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。

在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。

我们可以对上面代码进行如下修改:

http://img1.sycdn.imooc.com/5fbb95e50001faf709250262.jpg

上面的例子,首先有执行上下文f1,在f1中定义了函数f2,而通过对外返回f2的方式让f2得以执行。当f2执行时,访问了f1内部的变量a。因此这个时候闭包产生

闭包就是函数f1,

http://img4.sycdn.imooc.com/5fbb96bb00011a2609520693.jpg

在上面的图中,红色箭头所指的正是闭包。其中Call Stack为当前的函数调用栈,Scope为当前正在被执行的函数的作用域链,Local为当前的局部变量。

JavaScript拥有自动的垃圾回收机制,关于垃圾回收机制,有一个重要的行为,那就是,当一个值,在内存中失去引用时,垃圾回收机制会根据特殊的算法找到它,并将其回收,释放内存。

而我们知道,函数的执行上下文,在执行完毕之后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在,会阻止这一过程。

我们再来看个例子:

http://img4.sycdn.imooc.com/5fbb973400019aeb09300375.jpg

在上面的例子中,foo()执行完毕之后,按照常理,其执行环境生命周期会结束,所占内存被垃圾收集器释放。但是通过fn = innerFoo,函数innerFoo的引用被保留了下来,复制给了全局变量fn。这个行为,导致了foo的变量对象,也被保留了下来。于是,函数fn在函数bar内部执行时,依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。

这样,我们就可以称foo为闭包。

所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。比如在上面的例子中,我们在函数bar的执行环境中访问到了函数foo的a变量。个人认为,从应用层面,这是闭包最重要的特性。利用这个特性,我们可以实现很多有意思的东西。

不过读者朋友们需要注意的是,虽然例子中的闭包被保存在了全局变量中,但是闭包的作用域链并不会发生任何改变。在闭包中,能访问到的变量,仍然是作用域链上能够查询到的变量。

对上面的例子稍作修改,如果我们在函数bar中声明一个变量c,并在闭包fn中试图访问该变量,运行结果会抛出错误。

http://img1.sycdn.imooc.com/5fbb976800017f8c09270418.jpg

闭包形成的条件

  • 函数嵌套

  • 内部函数引用外部函数的局部变量

闭包的应用场景

初级前端了解以上闭包面试即可,以下内容属于中高级前端理解范围。大家可以暂时跳过,等学有一定基础积累,再回头看看。

除了面试,在实践中,闭包有两个非常重要的应用场景。分别是模块化与柯里化。

柯里化

模块化

在我看来,模块是闭包最强大的一个应用场景。如果你是初学者,对于模块的了解可以暂时不用放在心上,因为理解模块需要更多的基础知识。但是如果你已经有了很多JavaScript的使用经验,在彻底了解了闭包之后,不妨借助本文介绍的作用域链与闭包的思路,重新理一理关于模块的知识。这对于我们理解各种各样的设计模式具有莫大的帮助。

http://img2.sycdn.imooc.com/5fbb978f00019e5209240372.jpg

在上面的例子中,我使用函数自执行的方式,创建了一个模块。add是模块对外暴露的一个公共方法。而变量a,b被作为私有变量。

http://img1.sycdn.imooc.com/5fbb97c10001273f12400625.jpg

为了验证自己有没有搞懂作用域链与闭包,这里留下一个经典的思考题,常常也会在面试中被问到。

利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5

http://img1.sycdn.imooc.com/5fbb97e700010e5c09290154.jpg



这篇关于闭包原理以及使用场景的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程