本文引用https://zhuanlan.zhihu.com/p/25655417博客
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
要想理解闭包这个词,我们先来了解一下闭包的官方解释。
“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
但是我面试过程中听到一个小哥说的闭包形成的条件,就是函数返回函数,这是我听到的最容易理解的话。
官方的解释对于新手是非常难理解的,我建议把这句话拆开来理解。
这句话去掉形容词,就说闭包是表达式。怎么理解闭包就是一个表达式,那就看看常见的闭包代码吧。
function a(){ var i=0; function b(){ alert(++i); } return b; }
执行了a()()就是一个闭包。一般闭包就是一个函数,闭包还可以是下面的这种形式。
(function(){vari=0;functionb(){alert(++i);}returnb;})()();
在这里首先明白了闭包是一个什么主体了,这时候就开始了解闭包的形容词。我们一个一个的理解。理解形容词就要像剥洋葱一样去理解,先理解最外面的形容词。闭包是一个拥有环境的表达式。这里对于环境的理解是最重要的。
谈到闭包就必须要了解js的作用域和变量提升的概念。js的作用域和变量提升概念细看可以到我的博客里仔细看一遍(后面持续更行)。这里简单的说一下变量的作用域和变量提升。
变量作用域:
首先js不同java和C这类的语言,js是没有块级作用域的。js是函数作用域。所谓的函数作用域就是在一个函数内不管哪里定义的变量都可以在该函数内访问。函数里是可以访问函数外的变量,但是函数外就不能访问函数里的显示定义变量。自然,全局变量就不在这个范围考虑了。
变量提升:
如果深刻理解了变量作用域,那么变量提升也很简单了。js可以在第一行访问最后一行定义的变量,访问的只是声明的变量,赋值还是要按照代码一行一行执行。为什么js可以在一个函数里任何地方访问任何地方的变量(这里的任何地方排除函数里的函数),那是因为js函数作用域内把var声明的变量都放在了执行语句前面。下面两个例子来阐释变量提升。
function(){alert(a);//弹出undefindvara=1;}
其实变量提升的本质是:
function(){vara;alert(a);//弹出undefinda=1;}//这样是不是看的更加明白了。
但是这里变量提升只是打个比喻,好让大家明白js的函数作用域。其实实质不是这样的,实质是每个函数都有个内存区域来存储变量名。也就是一个存储变量名的环境。在函数执行之前就已经把环境创建出来了。函数中如果要用到一个变量,那么就会先到变量环境中查找,如果查找不到就往上查找,最后一直找到本地环境中。存储变量名的内存区域其实就是作用域里的最核心概念了。
到了这里是否理解了闭包是一个拥有环境的表达式这句话。这时候剩下的形容词,许多变量是不是很容易理解。
我们顺着来理解一遍,闭包就是一个拥有了环境绑定了许多变量的表达式。这样是不是很容易理解了。
到这里还不算了解完闭包,知道了闭包是什么就要知道闭包是干嘛用的。
闭包的作用:就是在a执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。
闭包的应用场景:
保护闭包里的变量安全。因为只有闭包里的函数才能访问闭包里的变量,所以外界就不能直接对闭包里的变量进行操作。
在内存中维持一个变量。单例模式是其中的经典模式。