require、loadfile、dofile、load、loadstring

x33g5p2x  于2022-08-17 转载在 其他  
字(3.5k)|赞(0)|评价(0)|浏览(301)

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填充。

_ENVlua5.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

可以看到:

  1. 代码块中,可以调用print函数,这就是因为默认env传入的是_ENV,相当于调用_ENV.print
  2. a_ENV.a 输出相同,其实在编译的时候,全局变量默认都会在前面加一个_ENV.限定
  3. 编译并运行代码块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
  1. 可以看到代码块chunkTest中,a取的是自定义环境变量enva值,打印a = 18
  2. 因为这里没有用默认全局环境变量_ENV填充,所以外部变量 a 并没有被修改: a = 100
  3. env中的 print = print,等价于:print = _ENV.print

相关文章

微信公众号

最新文章

更多