Lua中的全局变量不需要声明就可以使用。对于小程序十分方便,但是大型程序中 一处简单的笔误就可能造成难以发现的bug。

不过,这种性能可以改变。由于Lua将全局变量放在一个普通的table中,可以通过元表来改变其访问全局变量时的行为。

一种方法是简单地检测所有对全局table中不存在key的访问:

setmetatable(_G,{
__newindex = function(_, n)
error("attempt to write to undeclared variable " .. n ,)
end , --这里的逗号不能省,因为是在table构造式里哦
__index = function(_, n)
error("attempt to read undeclared variable " .. n, )
end , --这里的逗号不能省,因为是在table构造式里哦
}

执行这段代码后,所有对全局table中不存在的key访问都将引发一个错误:

print(a)        --> stdin:1: attempt to read undeclared variable a

但是该如何声明一个新的变量呢?

一种方法是使用rawset,可以绕过元表:

function declare(name ,initval)
rawset(_G,name ,initval or false) --这里的false确保 新变量不为 nil
end

另外一中更简单的方法就是只允许在主程序块中对全局变量进行赋值,那么当声明以下变量时:

a  =  

就只需检查此赋值是否在主程序块中。这可以使用debug库,调用debug.getinfo(2,"S")将返回一个table,

其中的字段what表示了调用元方法的函数是主程序块还是普通的Lua函数,或是C函数。

可以通过该函数将__newindex元方法重写:

__newindex = function(t,n,v)
local w = debug.getinfo(,"S").what
if w ~= "main" and w ~= "C" then --接收main和C代码的修改赋值
error("attempt to write to undeclared variable ".. n,)
end
rawset(t,n,v)
end

这个版本可以接收C代码的赋值,因为一般C代码都知道自己是做什么的。

为了测试一个变量是否存在,就不能简单地与nil 比较。因为如果它为nil ,访问就会抛出一个错误,这时同样可以使用rawget来绕过元方法:

if rawget(_G , var ) ==nil then
-- 'var' 没有声明
...
end

正如前面提到的,不允许全局变量具有nil 值,因为具有nil 的全局变量都会被自动认为是未声明的。

但要纠正这个问题并不难,只需引入一个辅助table用于保存已经声明变量的名称。

一旦调用了元方法,就检查该table,以确定变量是否已经声明过:

local declaredNames = {}
setmetatable( _G , {
__newindex = function( t , n, v)
if not declaredNames[n] then
local w = debug.getinfo(,"S").what
if w ~= "main" and w ~= "C" then
error("attempt to write to undeclared variable " .. n, )
end
declaredNames[n] = true
end
rawset(t , n ,v ) --完成实际的设置
end ,
__index = function (_, n)
if not declaredNames[n] then
error("attempt to read undeclared variable " .. n, )
else
return nil
end
end ,
}
)

此时,即使是下面的赋值也可以起到声明全局变量的作用:

x = nil

上述两中方法所导致的开销基本可以忽略不计;

第一种方法中,完全没有涉及到元方法的调用。

第二种方法,只有当程序访问一个为nil的变量时才会去调用元方法。

以上内容来自:《Lua程序设计第二版》和《Programming in Lua  third edition》

Chapter 14_2 全局变量声明的更多相关文章

  1. Python基础(六)_全局变量声明、可变参数、关键字参数

    1. global声明全局变量 #声明name这个变量为全局变量,只是写在函数里面 #写代码时,尽量不要用全局变量,会一直占用内存.       ------->{'name':'abc','s ...

  2. js中三种全局变量声明方法

    声明方式一: 使用var(关键字)+变量名(标识符)的方式在function外部声明,即为全局变量,否则在function声明的是局部变量.该方式即为显式声明详细如下: <script> ...

  3. C语言的傻瓜式随笔(二):全局变量、预编译、goto

    函数的作用:可以实现代码的重用. 函数只需要定义1次,那么函数中的代码就可以随意的调用.       -某不知出处的基本概念 学而时习之,如有误笔,请指正 一.goto跳转语句 goto在C语言的作用 ...

  4. 变量声明和定义及extern 转载

    在讨论全局变量之前我们先要明白几个基本的概念: 1. 编译单元(模块):    在IDE开发工具大行其道的今天,对于编译的一些概念很多人已经不再清楚了,很多程序员最怕的就是处理连接错误(LINK ER ...

  5. 剖析javascript全局变量和局部变量

    首先要记住: javascript是弱类型语言,它只有一种变量类型(var),为变量赋值时会自动判断类型并进行转换. 全局变量和局部变量如何声明? 全局变量声明: 第一种方式(函数外) var a; ...

  6. C/C++定义全局变量/常量几种方法的区别

    在讨论全局变量之前我们先要明白几个基本的概念: 1. 编译单元(模块):    在IDE开发工具大行其道的今天,对于编译的一些概念很多人已经不再清楚了,很多程序员最怕的就是处理连接错误(LINK ER ...

  7. JavaScript学习01 语言简介、基本使用和变量声明

    JavaScript语言简介.基本使用和变量声明 JavaScript是网景(Netscape)公司开发的一种基于客户端浏览器.面向对象.事件驱动式的网页脚本语言. JavaScript的前身叫Liv ...

  8. 用extern定义全局变量

    1.extern的作用 extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b); 则告 ...

  9. 函数改变全局变量-JS

    切记,一定按三步走: 1. 全局变量声明 2. 函数声明 3. 函数调用 正确做法: var dataStr = null; function remoteCallback(data) { dataS ...

随机推荐

  1. Java中IO流

    * IO流用来处理设备之间的数据传输 * Java对数据的操作是通过流的方式 * Java用于操作流的类都在IO包中 * 流按流向分为两种:输入流,输出流. * 流按操作类型分为两种: * 字节流 : ...

  2. java基础第二天

    学习了关键字,标识符,数据类型.变量和常量,运算符和表达式,开始编写一些简单的输入输出运算的程序了.

  3. 2014 ACM/ICPC Asia Regional Beijing Site

    1001 A Curious Matt 1002 Black And White 1003 Collision 1004 Dire Wolf 1005 Everlasting L 1006 Fluor ...

  4. shell中bash的常见命令

    shell 在计算机科学中,Shell俗称壳,用来区别Kernel(核) Shell分类:1:图形界面shell:通过提供友好的可视化界面,调用相应应用程序,如windows系列操作系统,Linux系 ...

  5. OSI七层模型详解

    OSI 七层模型通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯,因此其最主要的功能就是帮助不同类型的主机实现数据传输 . 完成中继功能的节点通常称为中继系统.在OSI七层模型中,处于 ...

  6. linux统计单词数

    sort +awk+uniq 统计文件中出现次数最多的前10个单词 实例 cat logt.log|sort -s -t '-' -k1n |awk '{print $1;}'|uniq -c|sor ...

  7. background-size做自适应的背景图

    background-size做自适应的背景图 在我们做页面布局的时候往往会遇到这样的情况当我固定一个元素的尺寸,在像元素加入背景的时候发现背景图片的原始尺寸很大,当我把背景图写入时往往超过元素很大一 ...

  8. python(序列递归)【输出原子级别元素。。。】

    晚上回去复习下原来的资料,返现Codebook中有个关于“展开一个嵌套序列”的话题. 任务说明:序列中的子项可能是序列,子序列的子项仍可能是序列,以此类推,则序列嵌套可以达到任意的深度.需要循环遍历一 ...

  9. WebStorm和IntelliJIEDA软件注册码网站(手动填写)

    很多人都发现 http://idea.lanyus.com/ 不能激活了 很多帖子说的 http://15.idea.lanyus.com/ 之类都用不了了 选择 License server (20 ...

  10. iOS 打包上传AppStore相关(2)-Xcode相应配置

    上一篇描述了如何在AppleDeveloper创建Certificates.App IDs和Provisioning Profiles的过程.本篇将详细描述在Xcode部分我们需要做的配置. 1.配置 ...