lua中的setfenv和getfenv

設置函數環境——setfenv

  當我們在全局環境中定義變量時經常會有命名衝突,尤其是在使用一些庫的時候,變量聲明可能會發生覆蓋,這時候就需要一個非全局的環境來解決這問題。setfenv函數可以滿足我們的需求。

  setfenv(f, table):設置一個函數的環境

  (1)當第一個參數爲一個函數時,表示設置該函數的環境

  (2)當第一個參數爲一個數字時,爲1代表當前函數,2代表調用自己的函數,3代表調用自己的函數的函數,以此類推

  所謂函數的環境,其實一個環境就是一個表,該函數被限定爲只能訪問該表中的域,或在函數體內自己定義的變量。下面這個例子,設定當前函數的環境爲一個空表,那麼在設定執行以後,來自全局的print函數將不可見,所以調用會失敗。

-- 一個環境就是一個表,該表記錄了新環境能夠訪問的全部域
newfenv = {}
setfenv(1, newfenv)
print(1)        -- attempt to call global `print' (a nil value)

  我們可以這樣繼承已有的域:

a = 10
newfenv = {_G = _G}
setfenv(1, newfenv)
_G.print(1)        -- 1
_G.print(_G.a)        -- 10
_G.print(a)        -- nil 注意此處是nil,新環境沒有a域,但可以通過_G.a訪問_G的a域

  可以看到,新環境中可以訪問_G,但有一點就是_G中的所有函數必須手動調用,這樣其實很不方便。我們可以使用metatable來對上述代碼進行改進:

-- 任何賦值操作都對新表進行,不用擔心誤操作修改了全局變量表。另外,你仍然可以通過_G修改全局變量:
newfenv = {}
setmetatable(newfenv, {__index = _G})
setfenv(1, newfenv)
print(1)        -- 1 新環境直接繼承了全局環境的所有域,好處:可以不需要通過_G來手動調用

  這樣,當訪問到函數中不存在的變量時,會自動在_G中查找。對於當前函數和_G都存在的變量,可以通過是否用_G顯示調用來區分,比如如果有兩個a,那麼_G.a表示繼承來的,a就是當前函數環境的。

  另外,可以通過getfenv(f)函數查看函數所處的環境,默認會返回全局環境_G。


  1. f = 4  
  2. function a()  
  3.     f = 3  
  4.     print(getfenv(0).f, getfenv(1).f, getfenv(2).f, getfenv(3).f)  
  5. end  
  6. A = {}  
  7. setmetatable(A, {__index = _G})  
  8. setfenv(a, A)  
  9.   
  10. function b()  
  11.     f = 2  
  12.     A.a()  
  13. end  
  14. B = {}  
  15. setmetatable(B, {__index = _G})  
  16. setfenv(b, B)  
  17.   
  18. function c()  
  19.     f = 1  
  20.     B.b()  
  21. end  
  22. C = {}  
  23. setmetatable(C, {__index = _G})  
  24. setfenv(c, C)  
  25. c()  

 

只有setfenv了環境。。getfenv才能生效。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章