閉包(closure)是由一個函數和該函數會訪問到的非局部變量(upvalue)組成,其中非局部變量是指不在局部做用範圍內定義的一個變量,但同時又不是一個全局變量,主要應用在嵌套函數和匿名函數中。咱們看下面的例子:數組
function new() local i = 0 return function() i = i + 1 return i end end local new1 = new() print(new1()) print(new1())
輸出:
閉包
1 2
上面的代碼中,new函數返回了一個函數,而這個返回的匿名函數就是閉包組合部分的函數,而i則是非局部變量。在看一下下面的例子:函數
function test(num) local function output() print(num) end return output end local fun1 = test(10086) fun1() local fun2 = test(10000) fun2()
這裏fun1以及fun2的函數體時相同的,都是output的函數體,可是輸出不一樣。由於fun1和fun2其實是兩個閉包,他們都擁有局部變量num的獨立實例。事實上,lua編譯一個函數時,會爲它生成一個原型,其中包括了函數體對應的虛擬機指令、函數用到的常量值和一些調試信息。在運行的時候,每當lua執行一個函數的時候,他會建立一個新的數據對象,其中包含了相應函數原型的引用以及一個由全部upvalue引用組成的數組,而這個數據對象就是閉包。因而可知,函數是編譯期的概念,而閉包時運行期的概念。
lua
非局部變量實際上指的是變量而不是值,這些變量能夠在內部函數之間共享,即upvalue提供一種閉包之間共享數據的方法。看下面的例子:調試
function share(n) local function fun1() print(n) end local function fun2() n = n + 1 end return fun1,fun2 end local f1,f2 = share(10086) f1() f2() f1() f2() f1()
輸出:
code
10086 10087 10088
f1,f2都是閉包,他們共享了一個非局部變量n,當lua發現兩個閉包的非局部變量指向的是當前堆棧上的相同變量時,只生成一個拷貝,而後讓這兩個閉包共享該拷貝,這樣任意一個閉包對該非局部變量就行修改都會影響到其餘閉包。
對象
閉包在建立的時候他的非局部變量就已經不在堆棧上的狀況也是有的,這是由於內嵌函數能引用更外層外包函數的局部變量,以下例:原型
function test(n) local function fun() local function inner1() print(n) end local function inner2() n = n + 1 end return inner1,inner2 end return fun end local t = test(10086) local f1,f2 = t() f1() f2() f1() local g1,g2 = t() g1() g2() g1() f1()
輸出:
虛擬機
10086 10087 10087 10088 10088
從輸出咱們能夠看出閉包f1,f2,g1,g2都共有同一個upvalue,這是由於在建立inner1,inner2這兩個閉包被建立時堆棧上根本未找到n的蹤跡,而是直接使用閉包fun的upvalue。t = test(10086)以後,t這個閉包必定已把n妥善保存好了,以後f一、f2若是在當前堆棧上未找到n就會自動到他們的外包閉包的upvalue引用數組中去找,並把找到的引用值拷貝到本身的upvalue引用數組中。因此f一、f二、g1和g2引用的upvalue實際也是同一個變量,而剛纔描述的搜索機制則確保了最後他們的upvalue引用都會指向同一個地方。io