第1章  名称

一般对函数式编程的介绍都会从一等值和纯函数等概念开始,本书却准备在那之前先花些篇章讨论两个通常未得到足够重视的主题:名称和类型系统。前者包括名称绑定、作用域和闭包等内容,后者包括类型的含义和划分、强类型和弱类型、静态类型和动态类型以及多态性的内容。理解这些概念对编程很有意义,无论使用的是哪种语言,采用的是什么范式。具体到本书的核心,使用JavaScript进行函数式编程,在对以上普适概念理解的基础上,掌握它们在JavaScript中的特定表现和行为,又有格外的重要性。这一方面是因为JavaScript长期以来被认为是一种简单的脚本语言,缺少在通用知识背景下对其特性和行为的分析,以致对其行为的认识往往是零碎的、实用的。另一方面是因为名称和类型系统与JavaScript的函数式编程有着紧密的关联。嵌套函数和闭包是JavaScript的函数式编程离不开的技术,鸭子类型是JavaScript藉以实现函数式编程通常具备的参数多态性特征的机制。这些内容都将在下面两章中得到充分的讨论。

1.1  名称绑定    
1.1.1  常量和变量    
1.2  作用域    
1.2.1  包块作用域与就近声明    
1.2.2  静态作用域和动态作用域    
1.2.3  前向引用和提升

1.3  闭包

名称绑定和作用域这两个概念看上去有些平凡,远没有闭包(Closure)引起的兴趣和疑问多。没有函数式编程经验的人,在初次接触到JavaScript的闭包概念时,多会觉得这是一个很新奇的东西,一时无法理解它的效果,也体会不了有经验的程序员所说的它带来的好处和运用它的技巧。而实际上如果掌握了名称绑定和作用域,就会发现闭包的出现是水到渠成的。

在程序运行中的某一刻或代码中的某一处,所有当前有效的名称组成的集合被称为此刻或此处的引用环境(Referencing environment)。当不针对某个名称时,我们把代码中引用环境保持不变的区域也称为作用域。这个意义上的作用域与前面讨论的名称的作用域是息息相关的,假如用前者来解释后者,它就是代码中所有作用域相同的名称所在的区域。根据名称作用域的规则,在全局代码中的某一处,引用环境就是全部全局名称组成的集合。在一个全局函数内,引用环境包括所有的局部名称、参数和全局名称。JavaScript的函数与C的一个巨大区别就是前者可以嵌套,也就是说一个函数可以声明在另一个函数内。一个内嵌函数的引用环境包括它自身所有的局部名称和参数、外套函数的局部名称和参数以及所有的全局名称。我们在本书后面会看到,嵌套函数是JavaScript编程中必不可少的写法,许多模式和技巧都是赖之以成立的。

内嵌函数需要能访问外套函数的引用环境,当内嵌函数在它的作用域内被直接调用时,满足这个要求是很平凡的。但是JavaScript中的函数还可以作为参数和返回值,这时从内嵌函数的声明到调用它的代码,引用环境发生了改变,若还要访问原来的引用环境,就必须以某种方式将内嵌函数的引用环境和它捆绑在一起,这个整体就称为函数的闭包。很多有关JavaScript的文章在介绍闭包时,都把它定义为从某个函数返回的函数所记住的上下文信息。一个函数可能成为返回值,确实是建立闭包的有力理由。因为函数的局部名称都存在于调用堆栈中,若没有闭包,外套函数返回内嵌函数后,外套函数的堆栈帧被删除,返回的内嵌函数所能引用的外套函数中的局部名称也将消失。

function createClosure() {let i = 1;return function () {console.log(i);}
}const fn = createClosure();
//若没有闭包,fn将无法引用createClosure的局部变量i。
fn();
//=> 1

但实际上闭包并不是只在函数被返回时才创建的,任何JavaScript的函数都是同它的闭包一同创建的。下面的代码不涉及返回函数,却显示了闭包的效果。

function f(fn, x) {if (x < 1) {f(g, 1);} else {fn();}function g() {console.log(x);}
}function h() {
}//假如没有闭包,此处的结果将会是1。
f(h, 0);
//=> 0

当函数g最终被调用时,参数x的值为1,但是g输出的x为0。这就是因为函数g使用的是它闭包中的x,而它的闭包是在声明函数时创建的,在第一次调用函数f时获得值0。等到f调用自身后再次进入其代码时,g的引用环境已经与声明它时的不同,参数x虽然名称相同,但与g闭包中的x是身份不同的值。

以上行为也可以用另一对概念来解释。上一节指出,在代码中遇到某个名称时,静态作用域使用的是空间上最近的声明,动态作用域使用的是时间上最近的声明。略加推敲会发现,在没有调用函数的情况下,代码的文本顺序和程序的执行顺序是一致的,空间上最近的声明就是时间上最近的声明,两种作用域方式的效果是相同的。假如所有的函数都在调用处声明,或者说在调用前内联化(Inline),也会导致同样的结果。两种作用域差别的关键就在于,函数的引用环境建立的时间,静态作用域是在函数声明时建立的,称为深绑定(Deep binding)【注:有些文献中将深绑定定义为在函数被作为参数传递时绑定引用环境,JavaScript中的函数可以作为返回值被赋值给变量,也可以作为对象的方法被动态调用,所以函数被作为参数传递时才绑定引用环境是不能满足所有情况的需求的。】;动态作用域是在函数执行时建立的,称为浅绑定(Shallow binding)。回看上面的代码,假如JavaScript采用的是浅绑定,函数g使用的就将是它执行时包围它的函数f的参数x,输出的结果将会是1。

在一个函数中,引用环境包括它的局部名称、参数和外套作用域中的名称(可能存在的外套函数的局部名称和参数以及所有的全局名称)。容易看出,闭包只需记住外套作用域的部分,因为函数自身的局部名称在每次运行时都会重新创建。

总而言之,闭包对于静态作用域来说并不是什么新的概念,而是以函数为中心的视角来看待静态作用域,或者说是在函数可以被传递、返回的语言中为了贯彻静态作用域的理念而采取的一种实现层面的技术。如果不关心关于静态作用域如何实现的细节,完全可以忽略闭包的概念。因为仅仅就理念上理解和分析代码中名称的含义而言,掌握静态作用域的理论就足够了。本书后面有用到闭包术语的地方,也只是为了强调它反映出的视角和语境。

1.3.1  包块作用域与闭包

1.4  小结

更多内容,请参看拙著:

《JavaScript函数式编程思想》(京东)

《JavaScript函数式编程思想》(当当)

《JavaScript函数式编程思想》(亚马逊)

《JavaScript函数式编程思想》(天猫)

《JavaScript函数式编程思想》——名称相关推荐

  1. 《JavaScript函数式编程思想》——递归

    第7章  递归 王二.张三和赵四一日无聊,决定玩击鼓传花讲冷笑话的游戏.王二和张三围成一圈传花,赵四负责击鼓.张三接连讲了几个诸如小菜.狐狸狡猾的笑话.花停在了王二的手中. 王二:这个笑话很短.你要保 ...

  2. 《JavaScript函数式编程思想》

    自序 伴随着Web技术的普及,JavaScript已成为应用最广泛的编程语言之一.由于其在Web前端编程中的统治地位.语言本身的表现力.灵活性.开源的本质和ECMAScript标准近年来的快速发展,J ...

  3. JavaScript函数式编程思想

    欢迎访问个人网站 最开始接触函数式编程的时候是在小米工作的时候,那个时候看老大以前写的代码各种 compose,然后一些 ramda 的一些工具函数,看着很吃力,然后极力吐槽函数式编程,现在回想起来, ...

  4. JavaScript 函数式编程思想

    来自 Professor Frisby's Mostly Adequate Guide to Functional Programming 英文版本 中文版本-版本较老 函数式编程是一种编程范式, 所 ...

  5. JavaScript函数式编程入门经典

    一个持续更新的github笔记,链接地址:Front-End-Basics,可以watch,也可以star. 此篇文章的地址:JavaScript函数式编程入门经典 正文开始 什么是函数式编程?为何它 ...

  6. 学会JavaScript函数式编程(第3部分)

    摘要: JS函数式编程入门. 原文:学会使用函数式编程的程序员(第3部分) 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 本系列的其它篇: 学会使用函数式编程的程序员(第1部分) 学 ...

  7. 一文带你了解 JavaScript 函数式编程

    (给前端大全加星标,提升前端技能) 作者:前端工匠 公号 / 浪里行舟 前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想. 本 ...

  8. 一文带你了解JavaScript 函数式编程

    前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想. 本文将略去那些晦涩难懂的概念介绍,重点展示在 JavaScript 中到底 ...

  9. 什么是JavaScript 函数式编程?

    前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想. 本文将略去那些晦涩难懂的概念介绍,重点展示在 JavaScript 中到底 ...

最新文章

  1. 表中查询重复的数据,如何通过sql语句查询?
  2. c++模板库的一些基本使用
  3. 走进移动web开发的四大框架
  4. mysql计算1天后的时间_mysql 计算某个时间,多少天后,多少个月后时间戳
  5. 别在.NET死忠粉面前黑.NET5,它未来可期!
  6. win7如何将计算机移至桌面,如何将win7电脑桌面的文件转移到其他盘中?
  7. redis配置文件的介绍
  8. @AspectJ中的几种通知方式详解
  9. 第一次HACK别人的DLL
  10. 地铁系统_北斗授时助力北京地铁地下定位系统
  11. DNW启动异常的问题
  12. Gateway过滤器详解
  13. Java开发人员2021年的职位描述和职责
  14. 实时翻译软件-大家都在用的实时免费翻译软件
  15. 只道情深,奈何缘浅!
  16. iOS常用的第三方库
  17. 12306的“短信公众号”到底是个啥?
  18. vue 中provide的用法_[转]浅谈vue中provide和inject 用法
  19. iOS中根据网络环境显示不同图片
  20. CAD中如何删除顽固图层?

热门文章

  1. android 打开公众号页面_微信公众号页面适配
  2. 嫖娼旷工被开除,员工不服上诉,法院判了···
  3. 大数据任务调度和数据同步组件初探
  4. vue使用Viewer.js
  5. Linux命令之移动文件与目录或重命名
  6. 摩托罗拉g7 plus,手机浮躁时代的匠心臻品
  7. Ubuntu中安装KDE桌面
  8. htmlcss小白笔记
  9. Mysql SQLyog连接远程数据库
  10. 【数据应用案例】美团外卖语音助手