2

温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命

闭包,好吃吗 ?

第一次听到这个词,很不幸是在一次面试中,可想而知结果很细碎,从此“闭包”和“跨域”在我匮乏的前端知识中成为了两座无法翻阅的大山,即便一度转设计,还是活在他们的阴影之下。

不过怎么说呢。缘,妙不可言,感谢某司让我猝不及防的重新走上了前端之路,既然又走回来了,一个前端工程师的(伪)的自尊告诉我这回必须要刚正面了

一听这个词,我就一脸懵逼,这是个名词,还是个动词?这是个包?还是个什么操作?我觉得对这个词本身的认识,在了解一个事物上非常重要,而且经验告诉我对于这种看上去就知道是被专(hu)业(bi)翻译过来的外来词汇,千万不要尝试通过中文字面意义去理解他,一定要去看看它在英文中的意思

closure 英[ˈkləʊʒə(r)] 美[ˈkloʊʒə(r)]
n.结束; (永久的) 停业,关闭; [电] 闭合; [数] 闭包;
vt. 使结束,使终止; 使停止辩论;

这下明白了,这货是个名词,这下对他能有一个客观的认识了,“闭包”这个词,他就是一个东西,一个结束了,关闭了,闭合了的东西

下面在看看*度百科上面定义

闭包包含自由(未绑定到特定对象)变量;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等语言中都能找到对闭包不同程度的支持。

怎么说呢,我坚信这不是人话,经过翻译,说了两件事:

  1. 闭包中有一堆不被释放的变量(这就明白什么为什么叫closure了)
  2. 很多语言都有(本文只看javascript)

闭包,什么样?

经过上面对闭包概念的初步理解,知道了这是一个包含一堆没有被释放的变量的东西,那这货在javascript中到底是以什么样的形式存在的呢?

知乎上的相关回答五花八门,从各个角度都有很是精彩但是容易懵逼
如何才能通俗易懂的解释javascript里面的‘闭包’? - 知乎
所以不妨先去看看大神相对“正统”博文
学习Javascript闭包(Closure)- 阮一峰
JavaScript之作用域与闭包详解 - 默语

总之,大家都提到了要从javascript变量的作用域的一个特点开始说起

变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
每次定义一个函数,都会产生一个作用域链(scope chain)。当JavaScript寻找变量varible时(这个过程称为变量解析),总会优先在当前作用域链的第一个对象中查找属性varible ,如果找到,则直接使用这个属性;否则,继续查找下一个对象的是否存在这个属性;这个过程会持续直至找到这个属性或者最终未找到引发错误为止。

可以理解为,javascript有一个特点,就是在函数中,有“作用域链”这么个东西,大概意思是,你作为函数中的一个变量,在找自己的值的时候,是从里往外找的,你要先看自己所在函数中有没有给你赋值,没有就往外走,看看你外面一层有没有,直到找到或者找不到报错为止。
因为有这个原理的存在,也就能解释为什么函数内部可以读取外部的变量了

下面我们来做个实验:

var a = "a";
var b = "b";
function test(){
    var c = "c";
    var b = "d"
    function testInner(){
        console.log(a);//a
        console.log(b);//d
        console.log(c);//c
    }
    testInner();
}
test();
console.log(b);//b,
console.log(d);//ReferenceError: d is not defined

结果可知:

  1. 函数内部可以读取外部函数变量,以及全局变量,并且是由内向外读取的
  2. 函数外部无法读取内部的变量(废话)

那如果我们要是想在外面get到函数内部变量的值呢?这个变量谁知道呢,答案是显而易见的

他的内部函数是可以读取他外部函数变量的,而这个内部函数就是一个闭包
//下文有更新

讲个故事来说,老王对自己的儿子小王(内部函数)是无话不说的,但是对自己的老婆(老王的上一层)却习惯性装傻,于是王太太就只能通过小王知道老王的私房钱在哪了,而这个带着私房钱信息的小王就是闭包。

这就又有一个问题了,王太太是怎么找到小王的呢/外部如何才能得到内部函数的呢,这简单,老王喝多了自己让王太太带小王去玩的/函数将内部函数作为返回值

看下面这个例子:

function getMoneyInfo(){
    var moneyInfo = "马桶水箱里";
    function askXiaoWang(){
        return moneyInfo
    }
    return askXiaoWang()
}
console.log(getMoneyInfo())//马桶水箱里
console.log(moneyInfo)//ReferenceError: moneyInfo is not defined

看到这里,感觉对闭包是个啥,以及是怎么来的有了个大概的认识,但是他具体是干啥的,有什么非用不可的好处,以及除了面试到底有什么应用场景,将在今后继续学习!

看到这里的都是真爱


20171126:更正与补充
经过对各大神的博文再次阅读,发现上文有些事情还是没说清楚:

既然closure有闭合和结束的意思,那到底是什么被关闭和结束了呢,也就是说是什么保持了一个不变的状态呢
是这个函数以及其所引用的变量

闭包的产生,之所以是closure,是因为由于在更外层调用了内部的函数,所以与这个函数所相关的一切都不能在外层工作完成前被释放,这也就是所谓的闭合和结束了,也就是会一直保持一种不变的状态。

说句人话,按照老王之前的习惯,私房钱(似有变量)用完了,下次再有私房钱(函数再次被调用)就换个地方(内存中),老婆根本不可能找得到,但是现在有了小王(内部函数)在,他一直被他妈安排盯着厕所水箱,老王就是想花钱都没法花了,对他来说,这点钱算是结束(closure)了,而在这看着钱的小王,就是一个所谓的闭包了


lskrat
478 声望43 粉丝

能不能借我个谱靠靠