Lua中全局变量与非全局环境介绍

今天来聊两个话题——全局变量和非全局环境。

正如大家目前心里所感受到的,全局变量的内容很简单,而非全局环境的内容就稍微要锻炼一下脑细胞了。

1.全局变量的原形

在Lua中,要声明全局变量很简单,那就是定义变量的时候,前面不要加上local。

这个神秘的全局变量,其实本质上也是一个table,它把我们创建的全局变量都保存到一个table里了。

而这个table的名字是:_G
 
我们来看看代码:

代码如下:

-- 定义一个全局变量
    gName = "哎哟,很挫哦";
  
    -- 用三种方式输出变量的值
    print(gName);
    print(_G["gName"]);
    print(_G.gName);

输出结果如下:

代码如下:

[LUA-print] 哎哟,很挫哦
[LUA-print] 哎哟,很挫哦
[LUA-print] 哎哟,很挫哦

我们定义了一个全局变量gName,于是这个gName成为了_G的一个字段。
怎么样,很简单吧。

2.非全局的环境

对于全局变量,不管到了哪个地方,哪种语言,大家总是会告诫说:“不要滥用,后果自负”
也许是因为这样,所以Lua有了一种比较特殊的机制:非全局环境。
我称它为“不会造成全局影响的全局变量”。

3.改变函数的全局变量环境——setfenv函数

先看看以下代码:

代码如下:

-- 定义一个全局变量
    gName = "哎哟,很挫哦";
  
    -- 将当前全局环境重新设置为新的table
    setfenv(1, {});
  
    -- 输出值
    print(gName);

如果现在运行代码,输出结果将会是这样的:

代码如下:

[LUA-print] LUA ERROR: [string "src/main.lua"]:107: attempt to call global ‘print' (a nil value)

为什么?很出乎意料的脸print函数都无法找到了?

这是因为我们已经把当前函数范围内的全局变量环境改变了,全局变量默认是保存在_G中的,而现在的全局变量是在一个新的table里。

目前这个table是空的,所以不存在任何全局变量。
 
setfenv函数就是用来改变某个函数范围里的全局环境的,通俗地说,就是把某个函数范围内的_G给弄没了。
 
setfenv函数两个参数分别代表:

1). 第一个参数,可以是即将要改变环境的函数,也可以是一个数字。数字1代表当前函数,数字2代表调用当前函数的函数,后面以此类推。

2).第二个参数,新的全局环境table。
 
4.保留原来的_G

现在连print函数都无法使用了,对于测试很不方便,我们可以做个小动作,把原来的_G保留起来。

如下代码:

代码如下:

-- 定义一个全局变量
    gName = "哎哟,很挫哦";
  
    -- 将当前全局环境重新设置为新的table
    setfenv(1, {g = _G});
  
    -- 输出值
    g.print(gName);
  
    -- 再次定义一个全局变量
    gName = "哎哟,有点错哦";
  
    -- 再次输出值
    g.print(gName);
  
    -- 输出原来的值
    g.print(g.gName);

只要在定义新的环境时,把_G作为一个字段放到新的table里,就可以调用原来的全局变量了。

那么,输出结果如下:

代码如下:

[LUA-print] nil
[LUA-print] 哎哟,有点错哦
[LUA-print] 哎哟,很挫哦

三次调用g.print函数的输出结果都是不一样的:

a.第一次,此时刚刚重新设置了全局环境,这时候当前函数的全局变量只有一个,那就是g,所以gName的值是nil。

b.第二次,我们再一次对gName进行赋值,此时,已经在新的环境中了,所以接下来输出的gName值是存在的。

c.第三次,这次输出的是g.gName的值,通过g调用的gName值是原先的全局环境里的值,所以gName的值仍然是最初的“哎哟,很挫哦”。
 
其实,这有什么用呢?倒不如直接用局部变量好了。

确实,从这例子里看不出什么特别的地方。

书里对于知识的介绍都是由浅入深的,所以这里暂时也没有更深入的介绍,看到后面内容的时候,我再继续和大家分享。

5.使用__index元方法保留原来的_G

这里还有一个小技巧分享一下,刚刚举例保留_G,但是调用print等函数时还需要形如g.print的方式,有点碍事。

我们可以利用__index来解决这个问题,如下代码:

代码如下:

-- 定义一个全局变量
    gName = "哎哟,很挫哦";
  
    -- 一个table,即将成为新的环境
    local newG = {};
    setmetatable(newG, {__index = _G});
  
    -- 将当前全局环境重新设置为新的table
    setfenv(1, newG);
  
    gName = "别再哎哟了,很烦!";
  
    -- 输出值
    print(gName);
    print(_G.gName);

我们给新的table设置一个元表,这个元表的__index元方法就是_G。

于是,当新的环境里找不到print字段时,就会去_G里寻找。
 
输出结果如下:

代码如下:

[LUA-print] 别再哎哟了,很烦!
[LUA-print] 哎哟,很挫哦

第一次输出的是新环境里的gName值,第二次输出的是原来环境里的gName值,互不影响。

6.结束

好了,关于全局变量和非全局环境,就暂时说这么多。

虽然暂时还感觉不到有什么作用,没关系,后面还会有关于这部分的内容。

就像__index一样,是基础,后面可能会经常提到。

(0)

相关推荐

  • Lua简介、编译安装教程及变量等语法介绍

    公司业务有用到Lua,与nginx结合起来实现高性能的web应用,想去弄懂这方面的知识,就去了解学习了下Lua,欢迎大咔们指教. 1. Lua介绍 Lua是一门简单的编程语言,它的优势在于可以整合C++的模块来扩展自身的功能,使用数百行或更少的代码来解决复杂的问题.具有的特征有: 1.1. 可扩展性:Lua从一开始就被设计成易于扩展的语言,很多功能都是通过外部库来实现的,很容易与其他编程语言如C.C++.Java等等进行交互. 1.2. 简洁: Lua十分简洁,但是功能强大,易于学习,很适合小规

  • Lua中的全局变量、非全局变量总结

    前言 Lua将其所有的全局变量保存在一个常规的table中,这个table称为"环境".这种组织结构的优点在于,其一,不需要再为全局变量创造一种新的数据结构,因此简化了Lua的内部实现:另一个优点是,可以像其他table一样操作这个table.为了便于实施这种操作,Lua将环境table自身保存在一个全局变量_G中.例如,我们可以使用以下代码打印当前环境中所有全局变量的名称. 复制代码 代码如下: for n in pairs(_G) do print(n) end 在你的电脑上运行一

  • Lua变量类型简明总结

    在上一节中说到了Lua的安装与变量,这节说说Lua变量的类型.Lua在使用中不需要预先定义变量的类型.Lua中基本的类型有:nil.boolean.number.string.userdata.function.thread.table.可以使用type函数来判断变量的类型. 1. nil nil是一个特殊的类型,用来表示该变量还没有被赋值,如果一个变量赋值为nil,可以删除这个变量. 2. boolean boolean类型的变量只有两个值:true和false.在条件表达式中非常有用的.在控

  • Lua极简入门指南:全局变量

    全局环境 Lua 把全局变量放在一个 table _G 中,这个 table 被叫做全局环境(global environment).打印所有的全局变量名: 复制代码 代码如下: for n in pairs(_G) do print(n) end _ENV(Lua 5.2 开始支持) 对于一个 free name(名字没有绑定任何声明)var 实际上会被转换为 _ENV.var(每个 chunk 中都会存在一个名为 _ENV 的变量): 复制代码 代码如下: v1 = 1 local v2 =

  • Lua中for循环语句的使用教程

    for循环是一个循环控制结构,可以有效地编写需要执行的特定次数的循环. 语法 Lua编程语言的for循环的语法是: 复制代码 代码如下: for init,max/min value, increment do    statement(s) end 下面是控制在一个循环的流程: 初始化步骤首先被执行,并且仅一次.这个步骤可让您声明和初始化任何循环控制变量. 接着是max/min,这是最大或最小值,直到该循环继续执行.它在内部创建了一个条件检查的初值和最大值/最小值之间进行比较. for循环体执

  • Lua判断变量是否为数字、字符串是否可以转换为数字等

    一.Lua 判断是字符还是数字 复制代码 代码如下: -- 如果待判断的是一个变量 local t = type(x); if t == "number" then  -- 是数字 else if t == "string" then  -- 是字符串 end -- 如果带判断是一个字符串,要判断是否可以转成数字, 则 local n = tonumber(x); if n then  -- n就是得到数字 else  -- 转数字失败,不是数字, 这时n == n

  • Lua中的基本语法、控制语句总结

    前言 学习一门语言,首先就是从最基本的语法开始,这一篇将对Lua中的语句进行概要的总结. 赋值 赋值的基本含义是修改一个变量或一个table中字段的值,这个和其它语言没有多少区别,但是对于Lua,有一个特性,它允许"多重赋值",也就是一下子将多个值赋予多个变量,例如以下代码: 复制代码 代码如下: local x1, x2 = 2, 4 print(x1)     -->2 print(x2)     -->4 在多重赋值中,Lua先对等号右边的所有元素求值,然后才执行赋值

  • Lua中创建全局变量的小技巧(禁止未预期的全局变量)

    Lua 有一个特性就是默认定义的变量都是全局的.为了避免这一点,我们需要在定义变量时使用 local 关键字. 但难免会出现遗忘的情况,这时候出现的一些 bug 是很难查找的.所以我们可以采取一点小技巧,改变创建全局变量的方式. 复制代码 代码如下: local __g = _G -- export global variable cc.exports = {} setmetatable(cc.exports, {     __newindex = function(_, name, value

  • 详解Lua中的while循环语句的使用

    在Lua编程语言中的while循环语句,只要给定的条件为真时将重复执行的目标语句. 语法 Lua编程语言中的while循环的语法是: 复制代码 代码如下: while(condition) do    statement(s) end 在这里,声明(S)可以是单一语句或语句块.该条件可以是任何表达式,并且真正是任意非零值.循环迭代当条件是true. 当条件为假,则程序控制进到紧接在循环之后的一行. 流程图: 这里,在while循环的关键点是,在循环可能不会永远运行.当条件测试结果为false,循

  • Lua基础教程之赋值语句、表达式、流程控制、函数学习笔记

    赋值语句 注释,单行用(--)来表示:多行用(--[[ ... ]])来标示: 定义,lua中没有定义(申明数据类型),它是通过赋值来确定其数据类型的. 赋值,是改变一个变量的值和改变表域的最基本的方法. a = "hello" .. "world" Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量.a, b = 10, 2*x <--> a=10; b=2*x 遇到赋值语句Lua会先计算右边所有的

  • Lua中的变量类型与语句学习总结

    1. Lua类型 1.1 基本类型 Lua是一种动态类型语言,没有类型定义的语法.Lua一共有8种基础类型:nil(空).boolean(布尔).number(数字).string(字符串).userdata(自定义类型).function(函数).thread(线程).table(表). 函数type可根据一个值返回其类型名称(字符串),如print(type(print)),输出"function":print(type(type(X))) ,输出"string"

随机推荐