1.loadfile只编译,不运行
loadfile(filename, mode, env)
用于加载一个指定路径filename
的代码块,参数如下:
基本和load
等同,区别是用来加载一个文件的代码块,参见如下:
LuaFunc.lua
(被访问)文件:
--LuaFunc.lua
function add(x, y)
return x + y
end
m = "LuaFunc.m"
local n = "n"
print("------------------ < LuaFunc.lua > ------------------")
print("m = "..m)
print("n = "..n)
print("------------------ < LuaFunc.lua > ------------------")
调用代码如下:
--加载并编译代码
f = assert(loadfile("Child/LuaFunc.lua"))
--运行
f()
print("add : "..add(100, 88))
print(m)
输出结果如下:
------------------ < LuaFunc.lua > ------------------
m = LuaFunc.m
n = n
------------------ < LuaFunc.lua > ------------------
add : 188
LuaFunc.m
loadfile:
只编译,不运行,它只会加载文件,编译代码,不会运行文件里的代码,loadfile
只会加载(load)lua
文件但是不会执行(run)
。比如,我们有一个hellofile.lua
文件:print(“hello”);
function hehe()
print(“hello”);
end
这个文件里有一句代码,和一个函数。试试用loadfile
加载这个文件,如下代码:
loadfile("hellofile.lua");
print("end");
输出结果如下:
end
如果说loadfile
会执行文件里的代码的话,那么,应该会输出hello
字符串的。结果表明,它是不会执行代码的。
2.dofile执行
dofile(filename)
用于加载一个指定路径filename
的代码块,并运行。参数如下:
内部先调用了loadfile
,然后会自动运行一下,参见如下:
dofile("Child/LuaFunc.lua")
print("add : "..add(100, 88))
print(m)
输出与上例等同。
dofile
就是执行代码,dofile与require
类似,但是它没有caching
已经执行过一次的文件,所以它可以重复执行多次。很明显,dofile就是会执行代码的家伙了,如下代码:dofile("D:/vscode lua代码/hello world/foo.lua");
print("end");
输出结果如下:
hello
end
3.require我只执行一次
require(modname) 用于加载一个指定的代码块,并运行。参数如下:
这个函数对于同一个代码块只会加载一次(加载过会保存起来),和dofile
类似,加载编译后也会自动运行,参见如下:
--加载编译并运行
require("Child/LuaFunc") -- 等同于:require("Child.LuaFunc")
print("add : "..add(100, 88))
print(m)
输出与上例等同。
require和dofile
有点像,不过又很不一样,require
在第一次加载文件的时候,会执行里面的代码。但是,第二次之后,再次加载文件,则不会重复执行了。换句话说,它会保存已经加载过的文件,不会重复加载。require
的返回值会被存储cache
起来,所以一个文件最多只会执行一次,即使被require
很多次。for i = 1, 2, 1 do
require("foo.lua");
end
print("end");
为了说明这种情况,我刻意调用了两次require
,输出结果如下:
hello
end
和我们说的一样,调用了两次,但是代码只执行了一次。如果这里换成dofile
,则会输出两次hello
字符串。
4.load用于加载字符串代码块
同loadstring
,5.2版本之后loadstring
被移除,用load
代替,load(chunk, chunkname, mode, env)
用于加载字符串代码块,参数如下:
这里要注意:load
用于加载字符串代码块并编译,但是并没有运行。在使用前要记得运行一下,参见如下:
a = 100
--加载字符串代码块
f = assert(load("a = a + 100"))
print("before a = "..a)
--运行代码块
f()
print("after a = "..a)
输出结果如下:
before a = 100
after a = 200
可以看到加载代码块后,在没有运行前,a
的值没有发生变化为100
,运行后执行了a = a + 100,a = 200
。
5.代码块chuck如何理解
lua
中的代码块chunk
,实际编译的时候,相当于被一个可变参匿名函数包装,并且会绑定一个唯一的upvalue值_ENV
,运行后将代码块chunk
中的全局变量添加到了全局环境变量_ENV
中。代码块 “a = a + 100
” 相当于如下函数:
--代码块
function chuck()
a = a + 100
end
--这句是比喻load代码块的加载编译过程(不一定恰当,意会就好)
f = chuck()
--运行
f()
这里有些时候可能会疑惑,load
中的这段代码块为什么能访问到外部的变量a?
env:
它是一个环境变量,可以自定义(一般用于模拟一个沙盒环境),默认这个值会用lua
的全局环境变量_ENV
填充。
_ENV
是lua5.2
版本才有的用来代替_G
的一个全局变量(_ENV
内部是指向_G
的,可以简单的认为他们是同一个东西),但它是一个upvalue
值。(_G
及_ENV
感兴趣的可以自行了解)这里简单描述一下_ENV
,每一个lua
代码块默认都会有一个_ENV
全局环境变量,它是一个表,包含了所有的lua
标准类库及所有编译过的全局变量。
a = 100
--加载并运行字符串代码块
assert(load("a = a + 11; b = 'b126'; print('this is chunk')"))()
print("a = "..a)
print("_ENV.a = ".._ENV.a)
print("b = "..b)
print("_ENV.b = ".._ENV.b)
输出如下:
this is chunk
a = 111
_ENV.a = 111
b = b126
_ENV.b = b126
可以看到:
代码块中,可以调用print
函数,这就是因为默认env
传入的是_ENV
,相当于调用_ENV.print
。
a
和 _ENV.a
输出相同,其实在编译的时候,全局变量默认都会在前面加一个_ENV.限定
。
编译并运行代码块chunk
后,变量b
已被加入到_ENV
中。
自定义环境变量env
的使用:一般用于模拟一个沙盒环境,参见如下:
a = 100
--自定义环境变量
local env = {
a = 8,
print = print
}
--加载并运行字符串代码块
assert(load("a = a + 10; print('this is chunk, a = '..a)", "chunkTest", "bt", env))()
print("a = "..a)
输出如下:
this is chunk, a = 18
a = 100
可以看到代码块chunkTest
中,a
取的是自定义环境变量env
的a
值,打印a = 18
;
因为这里没有用默认全局环境变量_ENV
填充,所以外部变量 a
并没有被修改: a = 100
env中的 print = print,等价于:print = _ENV.print
。