编译原理之美
宫文学
北京原点代码 CEO
46197 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 45 讲
开篇词 (1讲)
编译原理 · 期中考试周 (1讲)
编译原理之美
15
15
1.0x
00:00/00:00
登录|注册

10 | 闭包: 理解了原理,它就不反直觉了

修改程序体验高阶函数
示例:链表的高阶函数功能
返回新的函数
接受其他函数作为参数
实现高阶函数功能
实现面向对象编程
隐藏内部细节
信息封装
闭包函数的执行过程
环境变量的打包
外层函数退出后,内层函数仍能访问外层函数的变量
内层函数访问外层函数的变量
思考闭包的应用场景
作用域与this的关联
闭包与面向对象编程的关联
体验函数式编程
高阶函数
闭包的应用
闭包的特点
闭包的实现机制
闭包的内在矛盾
思考与总结
函数式编程
闭包
闭包与函数式编程

该思维导图由 AI 生成,仅供参考

在讲作用域和生存期时,我提到函数里的本地变量只能在函数内部访问,函数退出之后,作用域就没用了,它对应的栈桢被弹出,作用域中的所有变量所占用的内存也会被收回。
但偏偏跑出来闭包(Closure)这个怪物。
在 JavaScript 中,用外层函数返回一个内层函数之后,这个内层函数能一直访问外层函数中的本地变量。按理说,这个时候外层函数已经退出了,它里面的变量也该作废了。可闭包却非常执着,即使外层函数已经退出,但内层函数仿佛不知道这个事实一样,还继续访问外层函数中声明的变量,并且还真的能够正常访问。
不过,闭包是很有用的,对库的编写者来讲,它能隐藏内部实现细节;对面试者来讲,它几乎是前端面试必问的一个问题,比如如何用闭包特性实现面向对象编程?等等。
本节课,我会带你研究闭包的实现机制,让你深入理解作用域和生存期,更好地使用闭包特性。为此,要解决两个问题:
函数要变成 playscript 的一等公民。也就是要能把函数像普通数值一样赋值给变量,可以作为参数传递给其他函数,可以作为函数的返回值。
要让内层函数一直访问它环境中的变量,不管外层函数退出与否。
我们先通过一个例子,研究一下闭包的特性,看看它另类在哪里。

闭包的内在矛盾

确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

JavaScript中的闭包是一个重要概念,允许内部函数访问外部函数的变量,即使外部函数已执行完毕。本文通过示例展示了闭包的特性,并解释了闭包的内在矛盾和静态作用域的概念。作者指出,闭包的产生需要满足两个条件:函数要成为一等公民,同时需要延长某些变量的生存期。文章深入浅出地解释了闭包的实现机制,让读者更好地理解作用域和生存期,并能更好地利用闭包特性。此外,文章还介绍了函数作为一等公民的概念,并通过示例展示了在JavaScript和其他语言中如何使用函数作为基础类型。作者还讨论了在不同语言中声明函数类型变量的复杂性,并介绍了在playscript中实现闭包功能的方法。通过对闭包和函数作为一等公民的讲解,读者可以更好地理解JavaScript中的作用域和函数特性,为他们的编程实践提供了有益的指导。 文章还介绍了函数式编程的特点,以及高阶函数的功能。通过示例代码展示了高阶函数的应用,让读者更好地理解函数式编程的优点和实现方式。此外,文章还将闭包与面向对象编程进行了比较,指出闭包可以看作是一种对象,从而引出闭包与面向对象编程的相似之处。最后,文章鼓励读者深入理解作用域和生存期的概念,以便更好地掌握语言特性,如JavaScript中的this关键字。 总之,本文通过深入讲解闭包、函数式编程和面向对象编程的概念,为读者提供了全面的技术指导,帮助他们更好地理解和应用JavaScript中的重要概念和特性。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编译原理之美》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(19)

  • 最新
  • 精选
  • 刘強
    有些东西研究的透彻以后,你就会不由自主的成为哲学家了。

    作者回复: 我这几年对复杂系统有关的理论很感兴趣,曾经在校友的一次聚会上分享了一个主题,其中的主要意思,就是从科学甚至可以推导出哲学,印证古老智慧。

    2019-10-27
    3
    14
  • 独钓寒江雪
    闭包的产生(以JavaScript为例): 1. 因为JavaScript是静态作用域的,所以它内部环境中需要的变量在编译时就确定了,运行时不会改变; 2. 因为JavaScript中,函数是一等公民,可以被调用,可以作为参数传递,可以赋值给变量,也可以作为函数返回值,所以它的运行时环境很容易变化; 3. 当函数作为参数返回时,其外层函数中的变量已经从调用栈弹出,但是我们必须让函数可以访问到它需要的变量,因此运行时的环境和定义时的作用域之间就产生矛盾; 4. 所以我们把内部环境中需要的变量,打包交给内层函数(闭包函数),它就可以随时访问这些变量了,就形成了闭包。

    作者回复: 总结得非常好!Great!

    2020-01-15
    11
  • nil
    记得第一次遇到闭包是在学习python得时候,方式刚觉这个玩意好牛逼。后来随着对其理解的深入,闭包完全带有面向对象的意思,外层函数通过函数参数的形式给内部函数创建运营期变量,这个运行期作用的变量和oop中的成员变量有相似的味道。通过这一讲,对闭包的实现原理有了进一步的理解,原来闭包不反人类,设计还相当巧妙😁

    作者回复: 非常好! 遇到看似不正常的东西的时候,其实就是让认知深化的契机。

    2019-10-17
    3
    11
  • 沉淀的梦想
    闭包如果引用的是外部函数中的局部变量,直接把这个变量从栈中复制一份到FunctionObject里面就可以了,但是如果应用了全局变量的话,感觉必须要引用全局变量本身,这样才能自己的修改体现在全局变量中。老师代码中是如何实现这个的呢?

    作者回复: 非常好,你注意到了这个细节。 实际上,我在运行时用到了一个小技巧。首先是按照作用域查找变量,这个时候就会找到那个全局变量。在作用域里找不到的时候,再到FunctionObject中去找。所以,其实运行期里,全局变量存了两份。一份是在顶层的栈桢里,一份在FunctionObject里。只不过后者不起作用罢了。 当存在多层函数嵌套的时候,上面的算法可以根据运行时所在的作用域,访问正确的变量。 这个方法有些偷懒,因为毕竟FunctionObject里冗余了一份,浪费空间了。你也可以找其他机制来实现。只要支持闭包的原理就行! 你可以参考ASTEvaluator.java中的getLValue()方法,里面有注释,说了这个思路。

    2019-09-06
    6
    9
  • Smallfly
    我今天发现 IntelliJ 全家桶支持 ANTLR 插件,可以集成在编译器里直接查看生成的 AST。

    作者回复: 那更方便了!

    2019-09-22
    6
  • 怎么看起来像: 闭包变量,就是在语义分析时,为闭包函数生成的static变量。

    作者回复: 但只对这个闭包有用。再调用一次函数,新生成一个闭包,就会再生成另一个变量。

    2019-10-05
    2
    5
  • dbo
    其实,只要函数能作为值传来传去,就一定会产生作用域不匹配的情况,这样的内在矛盾是语言设计时就决定了的。 不理解这句话,为什么函数作为值传来传去会产生作用域不匹配的情况,考试能解释下吗?谢谢。

    作者回复: 有一个概念,叫做词法作用域(Lexical Scope),又叫做静态作用域。也就是变量的声明和使用的关系(变量消解),是在编译期就可以确定的,是完全由变量在源代码中的位置决定的。 对于闭包的情况,内部函数使用了外部函数的变量。但这个内部函数在运行时又被传到其他地方去使用。那么声明这个函数时所依赖的变量,在实际运行时,没有办法从上一级的栈帧里去获取,这就是矛盾的地方。

    2020-03-16
    3
  • Tao
    JavaScript 函数中 this ,如果是一个函数是对象调用比如 obj.foo(),那么foo中这个this就是当前对象obj 如果这个foo当作普通函数调用如: var bar =obj.foo bar() 这个时候this就不是obj这个对象了,非严格模式下this此时是全局对象 window

    作者回复: 从语言的设计角度讲,对象的方法和函数没有什么区别。 比如,java程序在调用方法时,会在第一个参数中传递对象引用,这就是this。比如一个void foo(int a)函数,实际会接收到两个参数。

    2020-02-23
    3
  • 曾经瘦过
    个人理解: JavaScript是 静态作用域 但是JavaScript中this 是动态作用域

    作者回复: 应该说,this本来就用来指代当前作用域的。对象就是一个作用域。所以this总在变是应该的。this不需要我们在代码里去声明,它是一个内在的机制。 动态作用域,是指我们在代码里显式声明的变量,其值不是声明时的作用域里的值,而是运行环境的作用域里的值。

    2019-09-25
    3
    3
  • Nail
    我用的是 ts,从 7 开始,发现很难跟上了。举个例子,比如在实现 visitor 时的很多方法都和 java 的不一样,也没有找到对应的文档。想请问有没有缓解的办法

    作者回复: TS实现visitor也很简单。一个办法,是让Antlr给你生成一下,然后你可以借鉴一下。Antlr支持TS。

    2020-07-09
    2
收起评论
显示
设置
留言
19
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部