# 局部變量
#### 定義
Lua中的局部變量要用local關鍵字來顯示定義,不用local顯示定義的變量就是全局變量:
~~~
g_var = 1 -- global var
local l_var = 2 -- local var
~~~
#### 作用域
局部變量的生命周期是有限的,它的作用域僅限于聲明它的塊(block)。 一個塊是一個控制結構的執行體、或者是一個函數的執行體再或者是一個程序塊(chunk)。我們可以通過下面這個例子來理解一下局部變量作用域的問題:
> 示例代碼test.lua
~~~
x = 10
local i = 1 --程序塊中的局部變量i
while i <=x do
local x = i * 2 --while循環體中的局部變量x
print(x) --打印2, 4, 6, 8, ...(實際輸出格式不是這樣的,這里只是表示輸出結果)
i = i + 1
end
if i > 20 then
local x --then中的局部變量x
x = 20
print(x + 2) --如果i > 20 將會打印22,此處的x是局部變量
else
print(x) --打印10, 這里x是全局變量
end
print(x) --打印10
~~~
#### 使用局部變量的好處
使用局部變量的一個好處是,局部變量可以避免將一些無用的名稱引入全局環境,避免全局環境的污染。另外,訪問局部變量比訪問全局變量更快。同時,由于局部變量出了作用域之后生命周期結束,這樣可以被垃圾回收器及時釋放。
在Lua中,應該盡量讓定義變量的語句靠近使用變量的語句,這也可以被看做是一種良好的編程風格。在C這樣的語言中,強制程序員在一個塊(或一個過程)的起始處聲明所有的局部變量,所以有些程序員認為在一個塊的中間使用聲明語句是一種不良好地習慣。實際上,在需要時才聲明變量并且賦予有意義的初值,這樣可以提高代碼的可讀性。對于程序員而言,相比在塊中的任意位置順手聲明自己需要的變量,和必須跳到塊的起始處聲明,大家應該能掂量哪種做法比較方便了吧?
“盡量使用局部變量”是一種良好的編程風格。然而,初學者在使用Lua時,很容易忘記加上“local”來定義局部變量,這時變量就會自動變成全局變量,很可能導致程序出現意想不到的問題。那么我們怎么檢測哪些變量是全局變量呢?我們如何防止全局變量導致的影響呢?下面給出一段代碼,利用元表的方式來自動檢查全局變量,并打印必要的調試信息:
#### 檢查模塊的函數使用全局變量
> 把下面代碼保存在foo.lua文件中。
~~~
module(..., package.seeall) --使用module函數定義模塊很不安全。如何定義和使用模塊請查看模塊章節
local function add(a, b) --兩個number型變量相加
return a + b
end
function update_A() --更新變量值
A = 365
end
getmetatable(foo).__newindex = function (table, key, val) --防止foo模塊更改全局變量
error('attempt to write to undeclared variable "' .. key .. '": ' .. debug.traceback())
end
~~~
> 把下面代碼保存在use_foo.lua文件中。該文件和foo.lua在相同目錄。
~~~
A = 360 --定義全局變量
local foo = require("foo") --使用模塊foo,如何定義和使用模塊請查看模塊章節
local b = foo.add(A, A)
print("b = ", b)
foo.update_A()
print("A = ", A)
~~~
運行use_foo.lua文件,輸出結果如下:
~~~
b = 720
lua: .\foo.lua:13: attempt to write to undeclared variable "A": stack traceback:
.\foo.lua:13: in function <.\foo.lua:12>
.\foo.lua:9: in function 'update_A'
my.lua:7: in main chunk
[C]: ?
stack traceback:
[C]: in function 'error'
.\foo.lua:13: in function <.\foo.lua:12>
.\foo.lua:9: in function 'update_A'
my.lua:7: in main chunk
[C]: ?
~~~
在 _update_A()_ 函數使用全局變量"A"時,拋出異常。這利用了模塊,想了解更多元表的內容,可以查看[模塊](#)章節。
Lua 上下文中應當嚴格避免使用自己定義的全局變量。可以使用一個 lua-releng 工具來掃描 Lua 代碼,定位使用 Lua 全局變量的地方。lua-releng 的相關鏈接:[http://wiki.nginx.org/HttpLuaModule#Lua_Variable_Scope](http://wiki.nginx.org/HttpLuaModule#Lua_Variable_Scope)
把lua-releng.pl文件和上述兩個文件放在相同目錄下,然后進入該目錄,運行lua-releng.pl,得到如下結果:
~~~
# ~/work/conf$ perl lua-releng.pl
WARNING: No "_VERSION" or "version" field found in `foo.lua`.
Checking use of Lua global variables in file foo.lua...
op no. line instruction args ; code
8 [7] SETGLOBAL 1 -4 ; update_A
2 [8] SETGLOBAL 0 -1 ; A
Checking line length exceeding 80...
WARNING: No "_VERSION" or "version" field found in `use_foo.lua`.
Checking use of Lua global variables in file use_foo.lua...
op no. line instruction args ; code
2 [1] SETGLOBAL 0 -1 ; A
7 [4] GETGLOBAL 2 -1 ; A
8 [4] GETGLOBAL 3 -1 ; A
18 [8] GETGLOBAL 4 -1 ; A
Checking line length exceeding 80...
~~~
結果顯示:在foo.lua文件中,第7行設置了一個全局變量update_A(注意:在lua中函數也是變量),第8行設置了一個全局變量A;在use_foo.lua文件中,第1行設置了一個全局變量A,第4行使用了兩次全局變量A,第8行使用了一次全局變量A。
- 序
- Lua簡介
- Lua環境搭建
- 基礎數據類型
- 表達式
- 控制結構
- if/else
- while
- repeat
- 控制結構for的使用
- break,return
- Lua函數
- 函數的定義
- 函數的參數
- 函數的返回值
- 函數回調
- 模塊
- String庫
- Table庫
- 日期時間函數
- 數學庫函數
- 文件操作
- 元表
- 面向對象編程
- FFI
- LuaRestyRedisLibrary
- select+set_keepalive組合操作引起的數據讀寫錯誤
- redis接口的二次封裝(簡化建連、拆連等細節)
- redis接口的二次封裝(發布訂閱)
- pipeline壓縮請求數量
- script壓縮復雜請求
- LuaCjsonLibrary
- json解析的異常捕獲
- 稀疏數組
- 空table編碼為array還是object
- 跨平臺的庫選擇
- PostgresNginxModule
- 調用方式簡介
- 不支持事務
- 超時
- 健康監測
- SQL注入
- LuaNginxModule
- 執行階段概念
- 正確的記錄日志
- 熱裝載代碼
- 阻塞操作
- 緩存
- sleep
- 定時任務
- 禁止某些終端訪問
- 請求返回后繼續執行
- 調試
- 調用其他C函數動態庫
- 我的lua代碼需要調優么
- 變量的共享范圍
- 動態限速
- shared.dict 非隊列性質
- 如何添加自己的lua api
- 正確使用長鏈接
- 如何引用第三方resty庫
- 使用動態DNS來完成HTTP請求
- 緩存失效風暴
- Lua
- 下標從1開始
- 局部變量
- 判斷數組大小
- 非空判斷
- 正則表達式
- 不用標準庫
- 虛變量
- 函數在調用代碼前定義
- 抵制使用module()函數來定義Lua模塊
- 點號與冒號操作符的區別
- 測試
- 單元測試
- API測試
- 性能測試
- 持續集成
- 灰度發布
- web服務
- API的設計
- 數據合法性檢測
- 協議無痛升級
- 代碼規范
- 連接池
- c10k編程
- TIME_WAIT問題
- 與Docker使用的網絡瓶頸
- 火焰圖
- 什么時候使用
- 顯示的是什么
- 如何安裝火焰圖生成工具
- 如何定位問題