参考资料
最后更新于:2022-04-01 03:27:06
## 参考资料
这篇文章是基于[Lua Style Guide](http://lua-users.org/wiki/LuaStyleGuide)而来。
语言的风格大致是通用的,在Python里,有一种叫pythonic的代码风格,详见:[让你的python代码更加pythonic](http://wuzhiwei.net/be_pythonic/)。
对于任何程序员,我都力荐[《代码大全》](http://book.douban.com/subject/1477390/)这本书。在里面,你可以找到十分完备的从设计,架构到具体编码,注释,到团队协作等等相关的引导。
还有几本书:[《程序员修炼之道》](http://book.douban.com/subject/5387402/),[《高效程序员的45个习惯》](http://book.douban.com/subject/4164024/),[《重构》](http://book.douban.com/subject/4262627/)。它们可以作为《代码大全》的补集存在。
关于《高效程序员的45个习惯》这本书,我进行了总结和提炼,阅读之前不妨看看这篇[读书笔记](https://github.com/iWoz/ReadingNotes/blob/master/%E9%AB%98%E6%95%88%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%8445%E4%B8%AA%E4%B9%A0%E6%83%AF.md)。
惯用法(精巧用法)
最后更新于:2022-04-01 03:27:04
## 惯用法(精巧用法)
### 尽可能使用local修饰变量(重要的事情要说三遍!)
原因:
* 使用`local`的变量会在作用域结束时释放其内存
* 使用`local`的变量会比全局变量的存取更快
* 全局变量会污染全局的命名空间,可能会导致诡异的bug出现
### 直接判断真假值
~~~
-- 不推荐
if obj ~= nil and willBreak == false then
-- ...
end
-- 推荐
if obj and not willBreak then
-- ...
end
~~~
原因:Lua在逻辑判断时将所有`非false`和`nil`的逻辑判断视为真,反之视为假,不需要再与布尔值和`nil`进行比对。
但是,在需要对`false`和`nil`进行区分时,需要写明`==`:`obj == nil`和`obj == false`。
### 默认参数的实现
范式:`param = param or defaultValue`
~~~
function setName(name)
name = name or 'noName'
-- ...
end
~~~
原因:`or`会在第一次为`true`的时候断路,返回其判断的最后一个值。所以当`name`为空时,`name or 'noName'`返回为`'noName'`,这会将`name`的值自动设置为`noName`。
### 一行代码实现表的拷贝
~~~
u = {unpack(t)}
~~~
### 一行代码判断表是否为空
用`#t == 0`并不能判断表是否为空,因为`#`预算符会忽略所有不连续的数字下标和非数字下标。
正确做法是:
~~~
if next(t) == nil then
-- 表为空
-- ...
end
~~~
因为表的键可能为`false`,所以必须与`nil`比较,而不直接使用`~next(t)`来判断表是否空。
### 更快的插入代码
~~~
-- 更慢,不推荐
table.insert(t, value)
-- 更快,推荐
t[#t+1] = value
~~~
原因:`[]和#`避免了高层的函数调用开销。
注释
最后更新于:2022-04-01 03:27:02
## 注释
通常在`--`前加上一个空格。
~~~
return nil -- not found (建议)
return nil --not found (不建议)
~~~
注释通常用在函数接口,或者复杂,精巧的逻辑上。
对于接口的注释,可以按照javadoc类似的来写。
~~~
-- Deletes a session.
-- @param id Session identification.
-------------------------------------
function delete (id)
assert (check_id (id))
remove (filename (id))
end
~~~
模块
最后更新于:2022-04-01 03:26:59
## 模块
Lua中有一个叫`module`的公有函数,此函数的作用是将一组变量和函数打包在一个模块名下,便于其他文件`require`。但是这个函数受到了诸多的[指责](http://lua-users.org/wiki/LuaModuleFunctionCritiqued),原因是其会创建一个公共变量,并且这个公共变量中的所有细节都会暴露出来。这其实十分不符合面向对象的规范。
以下有一种办法可以避免这个问题,即不采用`module`函数进行打包。
~~~
-- hello/mytest.lua
local M = {} -- 私有变量
local function test() print(123) end
function M.test1() test() end
function M.test2() M.test1(); M.test1() end
return M -- 关键
~~~
以下是导入此模块的方法。
~~~
local MT = require "hello.mytest"
MT.test2()
~~~
Lua内没有类这个变量类型,但是通过Lua的`metatable`可以轻松实现类的继承,多态等等特性。关于Lua中类的实现原理,请参考我之前写的这篇博客:[Lua中实现类的原理](http://wuzhiwei.net/lua_make_class/)。
作用域
最后更新于:2022-04-01 03:26:57
## 作用域
Lua的作用域以关键字`end`进行标识。
对于变量,有一条原则:在一切能使用`local`修饰的情况下,使用`local`进行修饰。
因为不用`local`修饰的变量会自动变成全局变量。全局变量十分危险,很容易被篡改而不知道在哪里被篡改了,这很容易导致顽固的bug出现。并且全局变量的处理速度也比局部变量的速度要慢很多。
所以,尽可能的用`local`来修饰变量。
有时候,用`do .. end`可以用来明确限定局部变量的作用域。
~~~
local v
do
local x = u2*v3-u3*v2
local y = u3*v1-u1*v3
local z = u1*v2-u2*v1
v = {x,y,z}
end -- x,y,z的作用域结束,被系统清理
local count
do
local x = 0
count = function() x = x + 1; return x end
end -- x的作用域结束,被系统清理
~~~
命名
最后更新于:2022-04-01 03:26:55
## 命名
最好的代码是自说明代码,这种代码不需要多余的注释,其本身便具备了描述作者意图的信息。一种好的命名风格是自说明代码的基础。
### 命名法
#### 驼峰命名法
* 小驼峰式命名法:第一个单字以小写字母开始;第二个单字的首字母大写,例如:`firstName`、`lastName`。
* 大驼峰式命名法:每一个单字的首字母都采用大写字母,例如:`FirstName`、`LastName`、`CamelCase`,也被称为Pascal命名法。
#### 下划线命名法
* 小下划线命名法:所有字母均为小写,例如登录按钮:`login_btn`。
* 大下划线命名法:所有字母均为大写,常见于常量,例如:最小间隔时间`MIN_GAP_TIME`。
采用驼峰法或者下划线法都不太重要,重要的是你采用了自己喜欢的一种命名法,然后一直保持下去。
### 变量名长度
通常作用域范围更大的变量名要比作用域范围更小的变量名具有更多的描述信息。例如:`i`经常用于循环中充当计数变量,而将其作为全局变量使用容易导致诸多问题。
### 变量命名
对于变量(包括函数),小驼峰式命名法或小下划线命名法是一个好选择。比如:`curSpeed`表示当前速度,`canDrop`表示是否能掉落等等。
对于布尔值型的变量,通常前缀加上`is`可以方便理解,比如`isRemoved`比`Removed`更加能表示这是一个布尔值变量。
Lua中有一种特殊的变量名:`_`,常用来表示可以被忽略的、不会使用到的变量,常使用在循环中。
~~~
-- `_`表示表的键可以被忽略,只在循环内使用表中的值`v`
for _,v in ipairs(t) do print(v) end
~~~
在表的循环中和函数参数列表中,`i`常表示`ipairs`下的数组下标,`k`常表示`pairs`下的键,`v`常表示对应的值,`t`则表示表。
~~~
for k,v in pairs(t) do ... end
for i,v in ipairs(t) do ... end
mt.__newindex = function(t, k, v) ... end
~~~
### 常数命名
Lua里没有严格的常数定义标识符,所以对于常数的命名格外重要。
常数一般采用大下划线命名法。这样每个字母都大写,十分醒目,且各个单词都用下划线分割,便于阅读。
比如:`MAX_SPEED`表示最大速度,`IS_SHOW_DEBUG_ERROR_MSG`表示是否显示报错消息等等。
### 类名
为了不与变量名和常数名混淆,类名通常使用大驼峰式命名法,即首字母大写。比如:`TouchManager`表示触摸管理器类。
### 包和模块名
包名和模块名通常很短,并且全部小写,单词间并没有下划线区分。比如:文件读取库名为`lfs`,表示`Lua File System`;XML解析库名为`lxp`,表示`Lua XML Parser`等等。
### 文件名
通常为了不与类名混淆,对于文件名,经常使用小驼峰式命名法或小下划线命名法。
写代码也有风格?
最后更新于:2022-04-01 03:26:53
> 原文出处:[Tim's Blog](http://wuzhiwei.net/lua_style_guide)
> 作者:吴智炜
## 写代码也有风格?
当然,写代码就跟写文章一样,每个人或多或少都有自己的风格。不同的语言也就像不同的文体一样,也有自己的独特的风格。Lua是一门脚本语言,写起来轻松惬意,但不代表它没有属于自己的风格指南。
好的代码风格基于可读性和一致性。代码更多的时间是给人看的,如果思考好了结构和逻辑,写代码的过程其实很快。风格的一致性也很重要,这样可以减少复杂度和理解成本。养成一种良好的代码风格会形成一种良好写代码习惯,这种习惯会使编码事半功倍。
下文将从命名,作用域,模块,注释和惯用法(精巧用法)等方面来说明Lua的代码风格,文章的最后会附上一些参考资料的链接以供读者拓展阅读。