19.Lua版本差异

19.Lua版本差异


19.1 知识点

判断Lua运行时

不同项目嵌的 Lua 运行时不一样。大部分语法按 Lua 5.1 跑通是没问题的,但是还是要看看实际运行时版本。

常见:LuaJIT、Lua 5.1~5.4、项目魔改 Lua、xLua / toLua / SLua 内置运行时。

第一步确认当前脚本跑在哪个 Lua 运行时上

print(_VERSION)

可能输出 Lua 5.1Lua 5.3 等。LuaJIT 环境下 _VERSION 也可能显示 Lua 5.1,所以还要看 jit.version

if jit then
    print(jit.version)
end

同时还要看项目接入文档、Lua DLL/so 版本、框架说明、标准库和语法支持情况。以项目真正跑起来的那个运行时为准。

版本 常见差异点 项目里怎么记
LuaJIT Lua 5.1 语义为主,内置 bit,带部分 Lua 5.2 / 5.3 扩展 不能当成完整 Lua 5.3 / 5.4;部分扩展要看 LuaJIT 版本和编译开关
Lua 5.1 unpacksetfenv/getfenvmodule 老项目、Unity Lua 热更、LuaJIT 体系里常见
Lua 5.2 _ENVgoto__pairs / __ipairs 环境机制从 setfenv 转向 _ENV,迭代元方法注意版本差异
Lua 5.3 整数 / 浮点、原生位运算、table.moveutf8 新运算和操作语法
Lua 5.4 incremental / generational GC、<close>__closecoroutine.close 资源关闭和协程关闭语义

LuaJIT

LuaJIT 是偏性能的 Lua 运行时,带 JIT 优化,接近 Lua 5.1。
内置 bit 库,位运算用 bit.band / bit.bor / bit.bxor
LuaJIT 2.1 带部分 5.2 / 5.3 扩展;
兼容行为要看编译开关。比如__pairs / __ipairs 得看 LUAJIT_ENABLE_LUA52COMPAT

Lua 5.1     :一套 Lua 语言版本和标准解释器。
LuaJIT      :主要按 Lua 5.1 语义,内部做了 JIT 优化和扩展。
Lua 5.3/5.4 :Lua 官方后续版本,有新语法和新标准库。

Lua 5.1

unpack

local t = { 1, 2, 3 }

print(unpack(t)) -- 1 2 3

5.2+ 用 table.unpack(t)。5.1里 unpack(t) 把连续数组拆成多个返回值。

setfenv和getfenv

环境是函数查全局变量表。没写 local 的变量会去默认全局表找:

value = 100

function TestEnv()
    print(value)
end

TestEnv() -- 100

setfenv 把查找环境换成指定表:

local env = {
    print = print,
    value = 123
}

function TestEnv()
    print(value)
end

setfenv(TestEnv, env)

TestEnv() -- 123

换了环境后,print 也要放进 env,否则报错:

local env = {
    value = 123
}

function TestEnv()
    print(value)
end

setfenv(TestEnv, env)

-- TestEnv() -- 报错,因为 env 里没有 print

getfenv 取出函数当前环境表:

local env = {
    print = print,
    value = 123
}

function TestEnv()
    print(value)
end

setfenv(TestEnv, env)

local currentEnv = getfenv(TestEnv)

print(currentEnv == env) -- true
print(currentEnv.value)  -- 123
  • setfenv(函数, 表):换查找环境。
  • getfenv(函数):拿当前环境表。

老 5.1 代码里还可能见到 module("M", package.seeall)是搞成一个表返回;新代码用 return 表导出模块。

5.2 改成用 _ENV,沙盒、限制全局访问会用到。

Lua 5.2

重点:_ENVgoto__pairs / __ipairssetfenv / getfenv 不再作常规写法。

_ENV

当前代码查非 local 变量用的那张表。

value = 100

function Test()
    print(value)
end

Test() -- 100
local env = {
    print = print,
    value = 123
}

local function Test()
    local _ENV = env
    print(value)
end

Test() -- 123

_ENV 换成 env 后,print 也去 env 里找,所以 env 里要有 print

local env = {
    value = 123
}

local function Test()
    local _ENV = env
    print(value)
end

-- Test() -- 报错,因为 env 里没有 print
Lua 5.1:setfenv 改函数环境。
Lua 5.2:_ENV 换查找环境。

_ENV 决定当前代码查非 local 变量时用的那张表。

goto

标签 ::name::,跳转 goto name

local i = 1

::begin::

print(i)
i = i + 1

if i <= 3 then
    goto begin
end

输出 123,效果和 while i <= 3 差不多。业务里不要滥用 goto,逻辑跳跃不好排查。

Lua 5.3

整数和浮点

区分 integer / float:

print(type(1))   -- number
print(type(1.0)) -- number
print(math.type(1))   -- integer
print(math.type(1.0)) -- float

原生位运算

print(1 & 3)  -- 按位与,输出 1
print(1 | 2)  -- 按位或,输出 3
print(1 ~ 3)  -- 按位异或,输出 2
print(1 << 2) -- 左移,输出 4
print(8 >> 1) -- 右移,输出 4
1 = 01,3 = 11

  01
& 11
----
  01  → 1

5.1 / LuaJIT 一般用 bitbit32 或项目封装`。

table.move

table.move(源表, 起始索引, 结束索引, 目标起始索引, 目标表)
local source = { 1, 2, 3, 4, 5 }
local target = {}

table.move(source, 2, 4, 1, target)

print(target[1]) -- 2
print(target[2]) -- 3
print(target[3]) -- 4
把 source[2] 到 source[4] 搬到 target[1] 开始的位置。
source[2]=2 -> target[1]
source[3]=3 -> target[2]
source[4]=4 -> target[3]

原表值还在:

print(source[2]) -- 2,原来的 source 里还在

utf8库

# 是字节长度,不是中文字符数。5.3 的 utf8 库补 UTF-8 工具:

local str = "你好"

print(#str)          -- 通常输出 6,因为 UTF-8 中文一般一个字 3 字节
print(utf8.len(str)) -- 输出 2,表示 2 个 UTF-8 字符

有些项目可能用自己封装的字符串工具。

Lua 5.4

分代GC

默认新对象大多活不久,所以它频繁只扫“新对象”做 minor GC,少量存活久的对象晋升为老对象,老对象不每次都扫,只在必要时做 major GC 全量回收


19.2 知识点代码

Lesson19_Lua版本差异.lua

print("**********Lua版本差异************")

print("**********知识点一 判断Lua运行时************")

-- _VERSION 看基础版本;LuaJIT 下也可能打印 Lua 5.1
print("当前 _VERSION:", _VERSION) -- 输出:Lua 5.1(本机示例)

if jit then
    print("当前运行环境:LuaJIT")
    print("LuaJIT 版本:", jit.version)
else
    print("当前运行环境:非 LuaJIT 或未暴露 jit 表")
end

-- 还要看接入文档、DLL 版本、框架说明、标准库和语法支持


print("**********知识点二 LuaJIT************")

-- 接近 5.1;位运算用 bit.band / bit.bor / bit.bxor,别默认能写 5.3 的 &、|、~
if jit then
    local bit = require("bit")

    -- bit.band:按位与,效果同 1 & 3
    --   01
    -- & 11
    -- ----
    --   01
    local value = bit.band(1, 3)

    print("bit.band(1, 3):", value) -- 输出:1
else
    print("当前不是 LuaJIT,跳过 bit 库示例")
end

-- LuaJIT 2.1 可能带部分 5.2/5.3 扩展;__pairs/__ipairs 看编译开关


print("**********知识点三 Lua 5.1************")

print("----------unpack----------")

local unpackFunc = unpack or table.unpack

if unpackFunc then
    local t = { 1, 2, 3 }

    print(unpackFunc(t)) -- 输出:1 2 3,5.1 用 unpack;5.2+ 用 table.unpack
else
    print("当前运行时没有 unpack / table.unpack")
end

print("----------setfenv和getfenv----------")

if setfenv and getfenv then
    local env = {
        print = print,
        value = 123
    }

    function TestEnv()
        print(value)
    end

    setfenv(TestEnv, env)

    TestEnv() -- 输出:123

    local currentEnv = getfenv(TestEnv)

    print(currentEnv == env) -- 输出:true
    print(currentEnv.value)  -- 输出:123
else
    print("当前运行时没有 setfenv / getfenv,5.2+ 用 _ENV")
end

-- 老 5.1 代码还可能见到 module("M", package.seeall),要认识;新代码用 return 表导出


print("**********知识点四 Lua 5.2************")

-- _ENV / goto 在 5.1 文件里不能直接跑,下面只做版本提示

--[[
local env = {
    print = print,
    value = 123
}

local function Test()
    local _ENV = env
    print(value) -- 输出:123
end

Test()
]]

--[[
local i = 1

::begin::

print(i) -- 依次输出:1 / 2 / 3
i = i + 1

if i <= 3 then
    goto begin
end
]]

print("5.2:_ENV 换查找环境;goto 做跳转;__ipairs 到 5.3 已废弃")


print("**********知识点五 Lua 5.3************")

print("----------整数和浮点----------")

print(type(1))   -- 输出:number
print(type(1.0)) -- 输出:number

if math.type then
    print(math.type(1))   -- 输出:integer
    print(math.type(1.0)) -- 输出:float
else
    print("当前运行时没有 math.type,跳过 integer / float 子类型")
end

print("----------原生位运算----------")

-- 5.3+ 才有 & | ~ << >>;5.1/LuaJIT 用 bit 库
--[[
print(1 & 3)  -- 输出:1
print(1 | 2)  -- 输出:3
print(1 ~ 3)  -- 输出:2
print(1 << 2) -- 输出:4
print(8 >> 1) -- 输出:4
]]

print("----------table.move----------")

if table.move then
    local source = { 1, 2, 3, 4, 5 }
    local target = {}

    table.move(source, 2, 4, 1, target)

    print(target[1], target[2], target[3]) -- 输出:2  3  4
    print(source[2])                       -- 输出:2,原表还在
else
    print("当前运行时没有 table.move")
end

print("----------utf8库----------")

if utf8 and utf8.len then
    local str = "你好"

    print(#str)          -- 输出:6,字节长度
    print(utf8.len(str)) -- 输出:2,UTF-8 字符数
else
    print("当前运行时没有 utf8.len")
end


print("**********知识点六 Lua 5.4************")

-- 5.4 分代 GC:新对象多走 minor GC,存活久的晋升老对象,major GC 全量回收
print("5.4 分代 GC:新对象多做 minor,存活久的晋升老对象,按需 major 全量回收")

if collectgarbage then
    print("collectgarbage 可用,当前内存约:", collectgarbage("count"), "KB")
else
    print("当前运行时无 collectgarbage")
end


print("**********总结************")

print("1. 先 print(_VERSION);LuaJIT 还要看 jit.version。")
print("2. LuaJIT 接近 5.1,位运算用 bit,别当成完整 5.3/5.4。")
print("3. 5.1:unpack、setfenv/getfenv。")
print("4. 5.2:_ENV、goto、__pairs/__ipairs。")
print("5. 5.3:integer/float、位运算、table.move、utf8。")
print("6. 5.4:incremental / generational 分代 GC。")
print("7. 以项目真正跑起来的运行时为准。")


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏