Lua-编译,执行和错误
目录
一、Lua的“编译”:把代码变成可执行的“函数”
1、dofile:简单但不灵活的“一键运行”
2、loadfile:更灵活的“编译后再执行”
3、loadstring:从“字符串”编译代码
4、三者的关系
二、编译的“坑”:作用域和性能
1.作用域问题(词法域VS全局编译)
2.“函数定义是赋值操作”的误解
三、更底层的 load 函数(了解即可)
四、错误处理:让程序更健壮
1、主动触发错误:error 函数
2、简化错误处理:assert 函数
3、补充细节
一、Lua的“编译”:把代码变成可执行的“函数”
Lua虽然是解释型语言,但它会把源代码预编译成中间形式(可以理解为“可执行的函数”),再执行。这部分核心是三个函数:dofile、loadfile、loadstring。
1、dofile:简单但不灵活的“一键运行”
作用:加载并运行一个Lua文件,相当于“打开文件->编译->执行”一步到位
实例:
dofile("test.lua")--直接运行test.lua中的代码缺点:如果文件加载失败,会直接报错;并且每次调用都要重新编译,效率不高。
2、loadfile:更灵活的“编译后再执行”
作用:只编译文件,不直接执行,返回一个编译后的函数。用户可以决定什么时候执行这个函数。
实例:
local f = loadfile("test.lua") --编译该文件,返回一个函数
f();  --手动调用编译后的代码
f();  --可以多次调用,不用重复重复编译优点:
编译一次,可以多次执行;
加载失败时会返回nil+错误信息,方遍自定义错误处理。
3、loadstring:从“字符串”编译代码
作用:和loadfile像似,但是他是从字符串中提取代码并运行,返回一个函数。
实例:
local f = loadstring("print("hello")") --把字符串里面的代码编译
f()  --编译执行分离4、三者的关系
dofile就是loadfile的简易包装
function dofile(filename)local f = loadfile(filename)f()
end二、编译的“坑”:作用域和性能
1.作用域问题(词法域VS全局编译)
loadfile编译代码时,不考虑“词法作用域”(即外层函数的局部变量),它编译的代码默认在全局环境中执行。
local i = 0
local f = loadstring("i = i + 1; print(i)")  -- 这里的 i 是全局的 i
local g = function() i = i + 1; print(i) end  -- 这里的 i 是外层的局部 if()  --> 1 (修改了全局 i)
g()  --> 2 (修改了外层的局部 i)2.“函数定义是赋值操作”的误解
Lua中加载代码块(如loadfile加载包含函数定义的文件)时,函数定义的本质是“赋值操作”,需要手动执行编译后才能完成定义。
--foo.lua文件内容
function foo(x)print(x)
end--main.lua的内容
f = loadfile("foo.lua")
print(foo) -> nil 还未定义
f()
print(foo) -> function的地址,完成了定义
print(foo(999))-> 999 函数可以正常调用三、更底层的 load 函数(了解即可)
 
loadfile 和 loadstring 都是基于 load 实现的。load 更灵活,它可以从 “自定义的读取器” 中获取代码(比如分块读取大文件),但平时用得少,知道即可。
四、错误处理:让程序更健壮
1、主动触发错误:error 函数
 
error 函数的作用是显式地在代码中抛出错误,中断程序执行并提示错误信息。
- 语法:error(错误消息 [, 错误级别])
- 示例(用户输入校验): lua 
 执行后,若输入非数字,程序会中断并提示print("enter a number:") n = io.read("*number") -- 读取数字,若输入非数字则返回 nil if not n thenerror("invalid input") -- 输入无效时,主动抛出错误 endinvalid input。
2、简化错误处理:assert 函数
 
assert 是 Lua 内置的 “断言函数”,用于简化 “条件不满足则抛错” 的逻辑,相当于 if not 条件 then error(消息) end 的简写。
- 语法:assert(条件, 错误消息)- 若 条件为true,返回条件的值;
- 若 条件为false或nil,则抛出错误消息。
 
- 若 
- 示例(简化上面的输入校验): lua 
 效果和用print("enter a number:") n = assert(io.read("*number"), "invalid input")error手动判断完全一致,但代码更简洁。
3、补充细节
- 错误级别的控制:error可选第二个参数控制 “错误在调用栈中的层级”,一般用默认值(2)即可,不用额外关注。
- 与元表的关联:文末注释提到 “这些操作可通过元表合法化”,比如对非 table 进行索引时,若元表定义了 __index,则不会直接报错(后续元表章节会详细讲)。
-- 捕获 loadstring 的错误
local f = assert(loadstring("i = i +", "语法错误的代码"))
-- 如果 loadstring 失败,assert 会抛出清晰的错误