关闭

谈一谈闭包

标签: 闭包
2689人阅读 评论(7) 收藏 举报
分类:

2017年度全网原创IT博主评选活动投票:http://www.itbang.me/goVote/167

每次打开Atom准备写文章的时候, 都要纠结如何开头… 烦~~

今天这篇文章我们来探讨一下闭包, 因为我在查阅很多资料时, 发现这些文章对于闭包的理解很多都是有出入的, 所以今天我们来探讨一下什么才是闭包. 当然, 这篇文章大多数是概念性的东西, 代码演示可能会涉及到几种不同的语言实现, 不过我会在代码开头标识出是哪种语言. 另外, 本文除了探讨闭包, 还可能会出现譬如柯里化等概念, 因为在这些概念里应用闭包可能是对闭包的作用的很好的说明. 最后需要说明的: 本文仅仅是我对闭包的理解, 当然可能存在不合理的地方, 不对的地方希望有人可以在评论中给我指出.

什么是闭包

好, 下面开始进入主题, 首先一个问题, 什么是闭包? 对于我们这些习惯了命令式编程, 尤其是java这种完全面向对象的语言的人, 闭包可能是一个很陌生的概念, 闭包时常在函数式语言中被提及, 那闭包到底是一个什么概念? 下面对闭包的解释来自维基百科.

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

闭包更多强调的是引用环境, 函数在定义时因为使用到了自由变量, 函数的调用和定义时的引用环境不同, 所以它必须要清楚的了解到自己定义时的引用环境, 在调用的时候需要切换到它定义的引用环境中执行, 这样就形成了闭包.

更通俗的讲, 因为函数引用了自由变量, 当它执行的环境和定义的环境不同时, 在执行它的地方就不仅仅是拿到这个函数这么简单了, 因为它必须要清楚它引用的自由变量, 所以最好的方式是把此时的引用环境和函数本身一并返回, 这样就是闭包.

所以, 闭包 = 函数 + 引用环境

分解一下就是闭包形成的必要条件.

  1. 函数引用自由变量
  2. 函数的执行环境和自由变量的声明环境不同

只有符合了以上两点, 函数的执行点才不仅仅需要函数本身, 而是函数+引用环境.

两个重点是自由变量, 引用环境, 但就这几个字可能对于一些对闭包一知半解的人来说确是很难理解, 因为在大部分人印象里闭包应该是这样的.

// kotlin
list.map { println it}

这样的一个实例中没有任何地方提及到自由变量. 其实这压根和闭包没有任何关系, 这仅仅是一种语法-lambda表达式, 很多人将lambda表达式想当然的认为是闭包. 再来看看下面语法.

// groovy
android {
  compileSdkVersion 24
}

对于搞android的人来说这行代码并不陌生, 很多人把这个也理解成闭包, 和上面kotlin的代码一样, 其实这和闭包也没有任何关系.

那什么样的才算闭包? 上面提到了, 引用了自由变量的函数, 这句话里有一个名词-自由变量, 那什么又是自由变量呢? 理解了这个名词后才能继续理解这句话.

在某个作用域内使用了其他作用域声明的变量, 那该变量就是自由变量

上面的概念是是我自己写的, 可能有点难理解, 来看看代码

// javascript
var a = 10;
function add5() {
  return a + 5;
}

上面的变量a即为自由变量, a变量不是在函数add5的作用域中声明的, 却在函数add5中被使用.

好了, 知道了什么是自由变量, 那下面我们来看看什么是闭包.

// javascript
function add(a) {
  return function(b) {
    return a + b;
  };
}

var add10 = add(10);
var result = add10(5);

上面的例子是最典型的闭包, add函数返回了一个函数, 这个匿名函数引用了add函数的一个a变量, 所以a变量是一个自由变量. 而且, 这个匿名函数的执行点不在变量a声明的作用域内, 根据上面的概念, 这样就形成了闭包.

再来看看下面的代码. 下面的代码来自apple官方的swift文档中闭包章节.

// swift
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

这样的算不算闭包? 30s思考一下, 在心里有答案了再往下看…










当然不是, 虽然是来自官方的文章, 但这里确实是对概念的错误理解. 将上面的代码普通化来看看.

// swift
func sorter(s1: String, s2: String) -> Bool {
  return s1 > s2
}

reversedNames = names.sorted(sorter)

一目了然, 上面的代码丝毫没有看到对自由变量的引用, 这里仅仅算是高阶函数罢了… 所以很多时候官方文档也是会误导人的…

相似的代码, 再来看看kotlin官方文档上闭包的实例

// kotlin
var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it
}
print(sum)

这是不是形成闭包了? 把它普通化来看看, 毕竟去除语法糖才更适合人理解.

// kotlin
var sum = 0
var filter: (Int) -> Bool = { a -> a > 0 }
var each: (Int) -> Int = { a -> sum + a }

ints.filter(filter).forEach(each)

主要看这个each函数, 很明显, 这里有对自由变量a的引用, 而且each函数的定义和执行时的引用环境不同, 所以上面的实例确实形成了闭包. 不过我还是要吐槽一下kotlin的文档, 它将闭包放在了Lambda章节里, 虽然实例代码确实是形成了闭包, 但肯定会去一些人产生误导作用, 毕竟是两个毫无关系的概念.

闭包的作用

通过概念和实例代码, 很明显闭包的存在改变了变量的生命周期, 大部分情况下它可以将自由变量的生命周期延迟到闭包函数的执行, 而函数式中最重要的一个思想是尽可能多使用纯函数(纯函数是指对于相同的输入必定有相同的输入的函数), 在纯函数中如果想要保持一个变量, 那闭包肯定是最佳选择. 来看一下实例.

// javascript
function nameBy(lastName) {
  return function(firstName) {
    retrn firstName + " " + lastName;
  }
}

var group = nameBy("Jordan")
var michael = group("Michael")
var susan = group("Susan")

另外一个函数式中重要的概念-柯里化大部分情况下也离不开闭包的支持, 什么是柯里化?

柯里化(英语:Currying),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

其实上面的大部分例子中已经实现了柯里化, 柯里化的好处就是大大的提高了函数的灵活性.

// javascript
var add = (x, y) => x + y

// user
alert(add(10, 20))
alert(add(10, 30))
alert(add(10, 40))

上面对add函数的调用其实都是10+y的形式, 很多时候我们为了封装, 又对10+y这样的式子进行封装.

// javascript
var add = (x, y) => x + y
var add10 = (y) => 10 + y

// user
alert(add10(20))
alert(add10(30))
alert(add10(40))

这里有一个不好的地方就是add10这个函数的封装只能适用于10+y, 虽然实现了柯里化, 但是对于使用者来说灵活性不够, 其实这里我们可以利用闭包对add函数稍加改造, 既方便使用又不失灵活性

// javascript
var add = (x) => (y) => x + y

// user
var add10 = add(10)
alert(add10(20))
alert(add10(30))
alert(add10(40))

再来看, 上面实例中的函数add的定义其实不太符合我们人类的思想, 这方面, 其实很多函数式编程语言中已经实现了自动柯里化, 来看个实例.

// elm
add x y =
 x + y

add 10 20 // result is 30
add10 = add 10
add10 20 // result is 30

这些支持自动柯里化的语言可以根据实参的个数推断出返回的类型, 所以可以很轻松的实现柯里化, 而不必在我们定义函数的时候过多考虑. 其实javascript也可以实现自动柯里化, 大家可以参考我的一个demo-https://github.com/qibin0506/js-curry.

好了, 虽然说了这么多关于柯里化的东西, 其实都是对闭包的一些应用, 可以帮助大家去理解闭包. 最后留几个代码片段, 大家判断一下是否实现了闭包.

// 片段 1
function f1() {
  return function() {
    return "hello world"
  }
}

f1()()
// 片段 2
function f1() {
  return function(arg) {
    return arg + 10
  }
}

f1()(20)
// 片段 3
var name = "qibin";
var f = function(name) {
  return name + " HI"
}

f(name)
// 片段 4
function someFunction() {
    var name = "qibin"
    var f = funtion() {
      return name + " HI"
    }
    f()
}
someFunction()
// 片段 5
var getName = function() {
  var name = "qibin"
  var f = function() {
    return name + " HI"
  }

  prntName(f)
}

function prntName(f) {
  console.log(f())
}
7
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

谈谈我对闭包知识的深刻理解

在javascript中闭包应该是最难理解的一部分内容。在我看来闭包就是和作用域之间的联系。 1、首先我们来了解一下javascript中的作用域知识。 javascript中的作用域其实就指...
  • z5234032
  • z5234032
  • 2016-09-19 17:59
  • 1589

Javascript 中 作用域、闭包与 this 指针

js 中的作用域,闭包的理解和运用,this 的理解和运用
  • JunBo_Song
  • JunBo_Song
  • 2016-08-20 14:17
  • 4382

请你谈谈对spring的理解?

1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是有这个IOC容...
  • qq_34360219
  • qq_34360219
  • 2016-10-14 22:27
  • 3887

谈一谈对JS闭包的理解

个人对前端的一些浅显的理解。
  • FE_HB
  • FE_HB
  • 2016-09-28 20:50
  • 580

谈一谈闭包

什么是闭包 好, 下面开始进入主题, 首先一个问题, 什么是闭包? 对于我们这些习惯了命令式编程, 尤其是Java这种完全面向对象的语言的人, 闭包可能是一个很陌生的概念, 闭包时常在函数式...
  • php_Zhaop
  • php_Zhaop
  • 2017-07-18 18:08
  • 269

谈一谈我与外包的情缘 --【叶子】

一、第一次接触外包         故事要从2006年的某一天说起,当时的我还是一名未毕业的大学生,被学校安排去一家公司实习(我觉得称之为“参观实习”更到位一些),那是一家以“对日外包”为主营业...
  • maco_wang
  • maco_wang
  • 2012-06-08 09:24
  • 9755

谈一谈网络编程学习经验

  • 2016-03-14 15:03
  • 1.02MB
  • 下载

让我们谈一谈手机病毒

  • 2010-11-29 17:12
  • 4KB
  • 下载

谈一谈cookie 和session 的区别

这些都是基础知识,不过有必要做深入了解。先简单介绍一下。 二者的定义: 当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cookie 会帮你在网站上所打的文字或是一些...
  • Sundefind
  • Sundefind
  • 4天前 08:03
  • 33

简单谈一谈压力测试

最近,在做API的压力测试,趟了不少坑,然后呢,简要记录一下。 压测前需要准备的一些事 拿到API文档不要立马上手,先基准测试,就是执行一次接口测试,至少要压这个接口,要先熟悉一下他的参数...
  • weiyi556
  • weiyi556
  • 2018-01-11 00:06
  • 14
    个人资料
    • 访问:644869次
    • 积分:6892
    • 等级:
    • 排名:第3919名
    • 原创:80篇
    • 转载:0篇
    • 译文:2篇
    • 评论:625条
    文章分类
    博客专栏
    友情链接

    鸿洋_

    Aggie的博客

    梁肖技术中心

    极客导航

    最新评论