4Programming in Lua(第4版)

状态与模块

模块化编程

模块环境全局变量require

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第4章:状态与模块

一、导读

1.1 本章学习目标

第4章讲解 Lua 的模块化编程:模块、环境、全局变量。

本章核心学习目标:

  • 掌握模块系统:require 和 module
  • 理解环境(environment)
  • 掌握全局变量的控制

1.2 为什么本章如此重要

模块化是组织大型 Lua 项目的基础。理解模块系统能写出可维护的代码。

1.3 核心问题引导

Lua 的模块本质上是什么?

require 如何查找和加载模块?

如何避免全局变量污染?


二、核心概念详解

2.1 模块

lua
-- mylib.lua
local M = {}

local private_var = "secret"

function M.public_func()
    return "public"
end

function M.another_func()
    return private_var  -- 可以访问局部变量
end

return M

-- main.lua
local mylib = require("mylib")
print(mylib.public_func())   -- public
print(mylib.private_var)     -- nil (不可访问)

2.2 require 机制

lua
-- require 的工作流程:
-- 1. 检查 package.loaded[modname]
-- 2. 如果不存在,查找 package.path
-- 3. 加载并执行模块文件
-- 4. 将返回值存入 package.loaded[modname]

-- package.path 示例
print(package.path)
-- ./?.lua;./?/init.lua;/usr/local/share/lua/5.4/?.lua;...

-- 加载 C 模块
-- package.cpath 控制 C 模块的搜索路径

-- 重新加载模块
package.loaded["mylib"] = nil
local mylib = require("mylib")  -- 重新加载

2.3 全局变量控制

lua
-- 检测未声明的全局变量
setmetatable(_G, {
    __newindex = function(t, n, v)
        if not rawget(t, "_declared") then
            rawget(t, "_declared")[n] = true
        end
        rawset(t, n, v)
    end,
    __index = function(t, n)
        if not rawget(t, "_declared") or not rawget(t, "_declared")[n] then
            error("attempt to read undeclared variable: " .. n, 2)
        end
    end
})

-- 声明全局变量
function declare(name, initval)
    rawset(_G, name, initval or false)
    if not rawget(_G, "_declared") then
        rawset(_G, "_declared", {})
    end
    rawget(_G, "_declared")[name] = true
end

三、实战案例

3.1 案例一:配置管理模块

lua
-- config.lua
local M = {}

local config = {
    debug = false,
    log_level = "info",
    max_connections = 100,
}

function M.get(key)
    return config[key]
end

function M.set(key, value)
    config[key] = value
end

function M.load(filename)
    local f = loadfile(filename)
    if f then
        local user_config = f()
        for k, v in pairs(user_config) do
            config[k] = v
        end
    end
end

return M

-- 使用
local config = require("config")
config.load("settings.lua")
print(config.get("debug"))

四、本章小结

模块是返回 table 的文件

require 缓存已加载的模块

避免全局变量,使用模块封装


五、思考与练习

如何实现模块的延迟加载?

如何防止模块被重复加载?


4/8状态与模
0%