名字空间、库

  
一、名字空间简介


名字空间是当前所有公用变量的默认环境表

环境表:是一个table类型的对象。
公用变量:指不是用local语句声明的变量,环境表中的公用变量也是这个talbe对象的成员变量.

我们默认在_G名字空间下工作,所以前有全局变量都可以写成_G.变量名字 或者 _G["变量名字"];

_x = 100;
_y = x;

也可以写成下面的格式

_G.x = 100;
_G["y"] = x;

因为默认名字空间是_G,所以大多时候我们都可以省略_G前缀.

大家使用过很多标准库的函数吧,
例如string就是一个名字空间,而string.left string.right等等函数都是string名字空间的成员。

名字空间可以很好的组织代码,不同的名字空间存在同名的成员变量或成员函数时,将能有效的避免名字污染

我们做一个通俗的比喻

张家也有个孩子叫小强,李家也有个孩子小强。
大家站在一起,你大声叫"小强",你说这到底是叫哪一个小强呢?这就是名字污染

张家,李家都是一个姓,一个人的实际上就是现实中的一种名字空间
而小强这个人可以看成现实中的对象,小强这个名字可以看成现实中的公用变量。

好了,现在你大声叫"张小强",就避免了名字污染。大家也知道你到底是叫哪一个小强了。
这就好比在变量名前右上名字空间前缀。

但是张小强在自已的家里,爸爸妈妈可以叫"小强",而不必叫"张小强".
好比一个变量在自已的名字空间里,可以省略名字空间前缀.

所以说句俗的可以的真理,名字空间就是一个变量的姓氏.

问题是这样我们还会碰到一个问题,世界上有很多姓张的,也有可能有很多的张小强,这怎么办呢?
这时候我们可以这样说"张老三家的小强",张是一个名字空间,张老三又是张下面的二级名字空间.
张老三自已是一个table变量,他又可以作为其他变量的名字空间

请看下面的代码

张 = _G; --我们估且让_G全局变也姓张吧
 
张.老三的家 = { 小强=0;小强弟弟=0 }; --这是"张老三的家",是一个table对象
 
张.老三的家.小强 = 110; --"张老三的家"又可以作为"张小强"的名字空间
张.老三的家.小强弟弟 = 119; --"老三的家"又可以作为"张小强弟弟"的名字空间
 
--可是这样写很复杂了,每个变量都要写太多的前缀.所以我们需要用namespace函数改变当前的默认名字空间
 
namespace("张.老三的家"); --改变当前的默认的名字空间,好比进入张老三的家里
小强 = 110; --LAScript会自动加上默认的前缀成为 张.老三的家.小强
小强弟弟 = 119;



下面介绍如何使用namespace函数进入新的名字空间。

二、namespace函数

namespace(ns,[tab[func]]...)指定或创建新的名字空间,
ns参数是一个字符串,指定名字空间的名字,可以用.来分隔多层名次空间。
如果名字空间不存在则创建新的名字空间. 如果名字空间已存在但是不是table对象则抛出命名冲突错误
然后将这个名字空间指定为当前作用域的默认名字空间。

可以在ns函数后面可以添加任意多个参数,当然也可以省略
如果参数为table类型(只能有一个这样的参数),名字空间将会继承这个table的所有成员(你只)
如果添加func参数,将会自动调用这个函数,并将当前创建的名字空间作为参数传递给这个函数。

例如代码: namespace("ns.ns2",_G);
创建一个ns.ns2名字空间,并作为当前作用域内所有变量的默认名字空间.
并且添加了一个table类型的参数_G. 如果在当前名字空间找不到成员会自动到_G中去查找成员并添加到当前名字空间.

例如代码: namespace("ns.ns2",func);
创建一个ns.ns2名字空间,并且执行函数 func(ns.ns2); func实际上是名字空间的构造函数

我们随时可以用 namespace("名字空间的名字") 进入任意的名字空间。

1、创建名字空间

namespace("ns1.ns2.ns") --创建并进入名字空间ns1.ns2.ns
     --注意,如果ns1.ns2.ns已经存在并且不是一个table,会抛出命名冲突错误。

     --进入ns1.ns2.ns以后,一些您常用的全局函数就不能直接访问了,因为他们在不同的名字空间。
     --所以模拟精灵 为每个名字空间创建了一个默认成员 _G 指向全局表。
     --在这里,可以如下调用全局对象

     _G.win.messageBox("我现在在ns1.ns2.ns名字空间里工作")

     -- 一种更快的方法,创建一个局部变量指向全局变量.
     -- 注意:访问局部变量比访问全局变量更快.因为不需要查找.


     local win = _G.win;
     win.messageBox("我现在在ns1.ns2.ns名字空间里工作")


namespace("table"); -- 进入已经存在的名字空间table
new = function(t)  --添加一个table.new 函数
    local obj =  {}
    
    if(t)then -- template table
        assert(type(t)=="table","table.new("..type(t)..") ! parameter is not a table",2);
        setmetatable(obj, t ) -- metatale
        t.__index = t; -- template table --
    end;
    
    return obj
end;


namespace("_G") --回到默认的全局名字空间

用namespace创建的名字空间默认有以下属性

_NAME 名字空间的完整名字
_PACKAGE 名字空间的路径名
_M 指向名字空间自已(环境表:table对象)
_G 指向全局环境表
namespace 名字空间默认都有namespace函数的一个引用.
import 名字空间默认都有import函数的一个引用.



2、给namespace添加参数

namespace("ns1.ns2.ns",_G)
    --指定继承_G的所有成员,这样就可以直接访问全局对象了
    win.messageBox("我现在在ns1.ns2.ns名字空间里工作")

    -- 但是并不推荐使用继承功能,一是会降低效率(因为查找成员时要到庞大的_G表中去查找)
    -- 二是会带来名字空间污染,一不小心覆盖了_G中的成员。


三、库(package)

LAS脚本库:如果用外部LAS文件创建名字空间,这个LAS文件称为LAS脚本库.

可以使用LAScript自行编写,官方也会提供一些脚本库,例如std脚本库.


DLL插件库:如果使用外部DLL插件创建名字空间,这个DLL文件称为DLL插件库.

通常由官方提供(与普通的DLL动态链接库不同).

四、从外部文件导入库

使用import函数导入外部LAS脚本库或者DLL插件库。
import(ns);
--ns参数指定名字空间,ns参数同时也指明文件的路径。

注意: import函数并不会重复的导入相同的库

import("std");  --找到std.las导入
import("std");  --std已经导入,忽略这句代码



例如 import("aa.bb");
import按以下顺序搜索

1、搜索文件顺序

aa\bb.las
aa\bb.dll
aa.dll
插件目录\libs.dll --这是默认的插件库

2、搜索目录顺序(?代替文件名称)

首先搜索LAS脚本库

当前目录 + ?.LAS;
脚本目录 + ?.LAS
小精灵插件目录(如果有) + Fairy_Ape\import\?.LAS
模拟精灵目录 + ?.LAS
模拟精灵插件目录 + Fairy_Ape\import\?.LAS;

如果没有找到文件,就继续搜索DLL插件库

小精灵插件目录(如果有) + Fairy_Ape\import\?.dll;
模拟精灵插件目录 + Fairy_Ape\import\?.dll;
脚本目录 + ?.dll
模拟精灵目录 + ?.dll
当前目录 + ?.dll

如果没有找到文件,就继续搜索标准库libs.dll

小精灵插件目录(如果有) + Fairy_Ape\import\libs.dll
模拟精灵插件目录 + Fairy_Ape\import\libs.dll

 

五、示例

import("std"); --从std.las导入标准脚本库
import("net"); --从net.dll导入.net插件库
import("test"); --导入test.las里的代码(即使不包含名字空间也会执行test.las)