1、在了解lua中的闭包之前,我们先来看一个栗子!!!
在上述例子当中,我们用lua的闭包实现了一个计数器。如果之前没有了解过闭包的概念的话,你一定会对上述代码的结果感到奇怪,下面我们就来详细的解释一下闭包。
2、第一类值(First class value )
lua中的函数就是所谓的”第一类值”。
在lua中定义一个函数就像创建普通类型值一样,lua中的函数就是一个值,它可以被存放在变量或数据结构中,可以被当做参数传给另一个函数,可以是另一个函数的返回值,还可以在运行期间被创建。
此外lua中的函数还可以嵌套定义,就是在函数里面定义函数,这也是lua闭包的基础。
3、词法定界
当一个函数内嵌套另一个函数的时候,内嵌套的函数可以访问外包函数的局部变量,这个特征就叫做词法定界。
4、闭包
表层理解闭包概念:
内嵌函数可以使用外包函数的局部变量,这种行为就叫做闭包。也就是词法定界。
深层理解闭包概念:
闭包=函数+upvalue。函数指的就是这些内嵌函数和外包函数。upvalue:内嵌函数可以访问外包函数已创建的所有局部变量,这些局部变量就被称为内嵌函数的upvalue。upvalue实际指的是变量而不是值,这些变量可以在内嵌函数之间共享,就好比C++类中的成员函数可以访问和修改成员变量一样。
可以看到,闭包实际上是数据和行为的结合体,就如同C++中的类一样,在某些情况下,我们需要记录某次调用完成以后数据的状态,就好比C++中的static变量一样,每次调用完成之后static类型的变量并不会被清除。
例:
在上述例子当中,upvalue实际上就是参数n,是一个局部变量。我们知道局部变量是保存在堆栈上的,只要upvalue不离开作用域,他就一直生存在函数堆栈上,这种情况下,闭包将使用指向堆栈上的upvalue的引用来访问他们。一但upvalue即将离开作用域,在从堆栈上消失之前,闭包将为upvalue分配新的空间并保存当前upvalue的值,以后便可通过新空间的引用来访问该upvalue。(如果用C语言来解释就是,通过指针访问upvalue这块空间的值,当离开作用域时,重新分配一块空间并将upvalue的值拷贝到里面,然后再让指针指向新分配的空间)
就如同上述例子一样,进入Increment之后闭包创建,这时n还没有离开作用域,所以还是使用堆栈上的n,当return之后离开作用域时,闭包会将n拷贝到自己管理的空间中。
5、闭包和函数的区别
实际上闭包只是在形式和表现上像函数,但实际上不是函数。
我们知道函数是是一些可执行的语句的组合体,这些代码语句在函数被定义后就确定了,并不会在执行时发生改变,所以函数只有一个实例。而闭包在运行时可以有多个实例,不同的upvalue和相同的函数组合可以产生不同的实例,就好像C++中相同的类代码可以创建不同的类实例一样。
例:下面实例和结果如图
lua编译一个函数时,其中包含了函数体对应的虚拟机指令、函数用到的常量值和一些调试信息。在运行时,lua每执行一个函数时,他就会创建一个一个新的数据对象,这个数据对象中包含了相应函数原型的引用、环境(用来查找全局变量的表)的引用以及一个由upvalue引用组成的数组,而这个数据对象就被称为闭包。由此可见,函数是编译期间的概念,是静态的,而闭包是运行期间的概念,是动态的。
6、闭包在迭代器中的应用
迭代器需要保留上一次调用的状态和下一次调用成功的状态,我们可以使用闭包机制来实现迭代器。
例:
结果:
|
请发表评论