闭包终结解惑
本文是对闭包(closure)的思考总结,其中夹杂着笔者对其的理解, 如若理解不到位,还望各位读者指出。
闭包是函数和声明该函数的词法环境的组合。
以上是MDN对闭包的最新解释, 这个概念高度抽象, 待笔者从代码角度进行阐述 首先从JavaScript代码引入, 现在考虑如下例子
var show = function() {
// str是创建在show函数中的局部变量, 其作用域在show函数内
var str = 'hello world'
// 返回了一个匿名函数, 且该匿名函数引用了show函数中的str局部变量
return function() {
console.log(str)
}
}
// 调用show方法, 返回一个函数类型的引用, 由fun变量接受
var fun = show()
// 调用fun指向的函数
fun()
从直观上看,该例子是在一个函数中返回了另一个函数。 show函数的返回值为函数类型,当调用show函数时返回的是另一个函数,而再调用这个返回的函数,则调用了在show函数中定义的匿名函数。该程序执行结果为在控制台输出了’hello world’,为了更直观的讲述这个例子,笔者将其改写
var show = function() {
// str是创建在show函数中的局部变量, 其作用域在show函数内
var str = 'hello world'
// 定义closure函数, 且该函数引用了show函数中的str局部变量
var closure = function() {
console.log(str)
}
// 将定义的函数返回
return closure
}
// 调用show方法, 返回一个函数类型的引用, 由fun变量接受
var fun = show()
// 调用fun指向的函数
fun()
该例子只是将匿名函数写成了实名的函数,程序执行效果不变。注意,当13行调用了show函数时, show函数内部的closure函数并没有调用,只是做了定义并返回给了fun变量, 在15行执行时才进行了调用。我们都知道在函数内部定义的局部变量, 当函数执行完后就应该释放了, 而15行执行时正确输出了str变量中的值, 说明局部变量中的str变量并未释放。这就是闭包带来的效果,如果把show函数看成是外部函数, closure函数看成是内部函数, 则这个内部函数即为闭包函数。所以闭包可以理解成定义在一个函数内部的函数。
而文章开头给出的闭包定义又作何解释呢?这是将闭包理解为了一种现象。从表现形式上看,闭包就是在一个作用域中访问了另一个作用域中的变量,即在全局处对局部的函数内的局部变量进行了访问。所以说闭包是由函数以及创建该函数的词法环境组合而成, 而这个环境包含了这个闭包创建时所能访问的所有局部变量。
以下是python中闭包 (注意下面例子中的str变量不符合命名规范,与str类型重名,不过以下例子只是为了演示,无伤大雅)
def show():
str = "hello world"
def closure():
print(str)
return closure
if __name__ == '__main__':
fun = show()
fun()
举了个与上面JavaScript例子相同逻辑的python程序代码, 其执行结果与相关解释都与JavaScript类似。
但需要注意的是在python3中引入了nonlocal
关键字, 先考虑如下代码
def show():
str = "hello world"
print("show's id = %d" % id(str))
def closure():
print("closure's id = %d" % id(str))
print(str)
return closure
if __name__ == '__main__':
fun = show()
fun()
运行上面程序后, 发现两个id是一样的,这符合我们对闭包的理解。
再考虑如下情况
def show():
str = "hello world"
print("show's id = %d" % id(str))
def closure():
str = 'closure'
print("closure's id = %d" % id(str))
print(str)
return closure
if __name__ == '__main__':
fun = show()
fun()
这段代码运行后,输出的两个id不同,且控制台输出的是’closure’,这说明了closure函数中定义的str变量对show函数中的str变量进行了覆盖。
最后考虑如下代码
def show():
str = "hello world"
print("show's id = " + id(str))
def closure():
print("closure's id = " + id(str))
print(str)
str = 'closure'
return closure
if __name__ == '__main__':
fun = show()
fun()
这段代码运行报错, 这是因为python解释器感知到了closure函数中局部变量的存在,则不使用show函数中的str变量, 但是str变量又定义在了使用之后,所以报错了。
那如果仍要使用外部函数中的变量呢? 解决办法是用nonlocal
关键字声明变量。
def show():
str = "hello world"
print("show's id = %d" % id(str))
def closure():
# 声明不使用该函数内的局部变量
nonlocal str
print("closure's id = %d" % id(str))
print(str)
str = 'closure'
return closure
if __name__ == '__main__':
fun = show()
fun()
这一段代码就会正确运行。
本文作者: Ifan Tsai (菜菜) 本文链接: https://www.caiyifan.cn/p/28440.html 版权声明: 本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!
相关文章
- Java高级编程:闭包
- js闭包面试题经典_js闭包原理
- 【说站】JavaScript闭包的缺点
- 闭包概念及面试题
- 【Groovy】循环控制 ( Number 注入函数实现循环 | times 函数 | upto 函数 | downto 函数 | step 函数 | 闭包作为最后参数可写在外面 )
- 【Groovy】闭包 Closure ( 自定义闭包参数 | 自定义单个闭包参数 | 自定义多个闭包参数 | 闭包参数默认值指定 )
- 【Groovy】xml 序列化 ( 使用 MarkupBuilder 生成 xml 数据 | 标签闭包下创建子标签 | 使用 MarkupBuilderHelper 添加 xml 注释 )
- 一种求离散数学传递闭包的算法java实现详解编程语言
- 深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解
- 基于javascript闭包基础分享
- javascript闭包传参和事件的循环绑定示例探讨
- js闭包实例汇总
- JavaScript中的闭包(Closure)详细介绍
- Javascript闭包用法实例分析
- 深入理解javascript作用域和闭包
- Javascript的setTimeout()使用闭包特性时需要注意的问题