目录
[三、更底层的 load 函数(了解即可)](#三、更底层的 load 函数(了解即可))
[1、主动触发错误:error 函数](#1、主动触发错误:error 函数)
[2、简化错误处理:assert 函数](#2、简化错误处理:assert 函数)
一、Lua的"编译":把代码变成可执行的"函数"
Lua虽然是解释型语言,但它会把源代码预编译成中间形式(可以理解为"可执行的函数"),再执行。这部分核心是三个函数:dofile、loadfile、loadstring。
1、dofile:简单但不灵活的"一键运行"
作用:加载并运行一个Lua文件,相当于"打开文件->编译->执行"一步到位
实例:
Lua
dofile("test.lua")--直接运行test.lua中的代码
缺点:如果文件加载失败,会直接报错;并且每次调用都要重新编译,效率不高。
2、loadfile:更灵活的"编译后再执行"
作用:只编译文件,不直接执行,返回一个编译后的函数。用户可以决定什么时候执行这个函数。
实例:
Lua
local f = loadfile("test.lua") --编译该文件,返回一个函数
f(); --手动调用编译后的代码
f(); --可以多次调用,不用重复重复编译
优点:
编译一次,可以多次执行;
加载失败时会返回nil+错误信息,方遍自定义错误处理。
3、loadstring:从"字符串"编译代码
作用:和loadfile像似,但是他是从字符串中提取代码并运行,返回一个函数。
实例:
Lua
local f = loadstring("print("hello")") --把字符串里面的代码编译
f() --编译执行分离
4、三者的关系
dofile就是loadfile的简易包装
Lua
function dofile(filename)
local f = assert(loadfile(filename))
f()
end
二、编译的"坑":作用域和性能
1.作用域问题(词法域VS全局编译)
loadfile编译代码时,不考虑"词法作用域"(即外层函数的局部变量),它编译的代码默认在全局环境中执行。
Lua
local i = 0
local f = loadstring("i = i + 1; print(i)") -- 这里的 i 是全局的 i
local g = function() i = i + 1; print(i) end -- 这里的 i 是外层的局部 i
f() --> 1 (修改了全局 i)
g() --> 2 (修改了外层的局部 i)
2."函数定义是赋值操作"的误解
Lua中加载代码块(如loadfile加载包含函数定义的文件)时,函数定义的本质是"赋值操作",需要手动执行编译后才能完成定义。
Lua
--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 then error("invalid input") -- 输入无效时,主动抛出错误 end执行后,若输入非数字,程序会中断并提示
invalid 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 会抛出清晰的错误