zoukankan      html  css  js  c++  java
  • Lua笔记——5.环境Environment

    Environments

    与储存在lua解释器中特殊数据结构里的本地变量不同,全局变量储存在一个表table中。而Lua的一个非常有用的特性就是每个function都可以去改变这个table,这些table中的每一个都称之为一个环境environment。所以改变了这个表的function可以看到一组不同的全局变量。

    Lua的默认全局环境即默认的全局表使用"_G"索引储存在它自己内部,Lua5.1和5.2之后版本中环境的工作方式非常不同

    Environments in Lua 5.2

    • Lua 保留一个独特的环境称之为全局环境,它的值保存在C 注册表中的一个特定索引中。在Lua里,变量_G被该值初始化称为全局环境。
    • 当Lua编译一个代码块,该代码块会被设置一个外部的局部变量_ENV(在一个代码块中_ENV本身并不是一个全局的名称)它会用全局环境_G 初始化该代码块的外部的局部变量_ENV upvalue。因此,代码块中任何一个对全局变量var的引用都会被语法转换为_ENV.var(默认的话即是_G.var)。
    • Lua中全部的标准库都在全局环境_G中被加载并且一些函数语句会被运行在全局环境中。可以使用load或者loadfile来加载代码块到一个不同的环境。任何一个被用作_ENV的值的table都称之为一个环境environment。

    当Lua编译或者load一个代码块等同于代码:

    --every chunk is compiled in the scope of an external local variable called _ENV
    local _ENV = _G
    return function (...) -- this is the function what's returned from load
        -- code you passed to load goes here, with all global variable names replaced with _ENV to lookups
        -- so, for example "a = b" becomes "_ENV.a = _ENV.b" if neither a nor b were declared local
    end
    

    _ENV:

    --file: testEnv.lua
    
    x = 300
    y = 100
    
    print(_ENV == _G)   -- prints true, since the default _ENV is set to the global table
    
    print("pos x : " .. x .." pos y : " .. y)   -- pos x : 300 pos y : 100
    
    local function setEnv(t)
        assert(type(t) == "table" , "Notice: The env you pass ".. type(t) .." is not a table .")
        local print = print
        local _ENV = t
        --get the local variable x , y  and print , as same as _ENV.x , _ENV.y , _ENV.print
        print("pos x : " .. x .." pos y : " .. y)   -- pos x : 800 pos y : 500
    end
    
    local env = {x =800 , y = 500 }
    setEnv(env)
    
    print("pos x : " .. x .." pos y : " .. y)   -- pos x : 300 pos y : 100
    

    load (ld [, source [, mode [, env]]])

    Loads a chunk.

    If ld is a string, the chunk is this string.

    If ld is a function, load calls it repeatedly to get the chunk pieces. Each call to ld must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk.If there are no syntactic errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message.

    If the resulting function has upvalues, the first upvalue is set to the value of env, if that parameter is given, or to the value of the global environment. (When you load a main chunk, the resulting function will always have exactly one upvalue, the _ENV variable . When you load a binary chunk created from a function , the resulting function can have arbitrary upvalues.)

    source is used as the source of the chunk for error messages and debug information . When absent, it defaults to ld, if ld is a string, or to "=(load)" otherwise.

    The string mode controls whether the chunk can be text or binary (that is, a precompiled chunk). It may be the string "b" (only binary chunks), "t" (only text chunks), or "bt" (both binary and text). The default is "bt".

    In most cases, you don't need to use environments, unless you want to sandbox a loaded chunk, to give it convenient access to certain functions by making them look global, or to prevent it from seeing unsafe functions for security reasons. This is why 5.2's load function takes a parameter that lets you set the chunk's _ENV to a custom table, instead of_G.

    loadfile ([filename [, mode [, env]]])

    Similar to load, but gets the chunk from file filename or from the standard input, if no file name is given.

    案例代码:

    --file :testSandEnv.lua
    
    local sandEnv = { os = os , print = print , _VERSION = _VERSION }
    
    local func = load("print(_VERSION);print(os.execute());","loadSandEnv",nil,sandEnv)
    
    if func == nil then
        error("Invalid syntax.")
    end
    
    func()
    

    运行结果:

    Environments in Lua 5.1

    • 每个function都可以有一个环境table与之相关联,并且可以通过setfenv以及getfenv来改变或者得到这个环境表
    --file: testEnv.lua
    
    x = 300
    y = 100
    print("pos x : " .. x .." pos y : " .. y)   -- pos x : 300 pos y : 100
    
    function setEnv(t)
        assert(type(t) == "table" , "Notice: The env you pass ".. type(t) .." is not a table .")
        local print = print
        setfenv(1,t)
        print("pos x : " .. x .." pos y : " .. y)   -- pos x : 800 pos y : 500
    end
    
    local env = {x =800 , y = 500 }
    setEnv(env)
    
    print("pos x : " .. x .." pos y : " .. y)   -- pos x : 300 pos y : 100
    

    load (func [, chunkname])

    Loads a chunk using function func to get its pieces. Each call to func must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk.

    If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.

    chunkname is used as the chunk name for error messages and debug information. When absent, it defaults to "=(load)".

    loadfile ([filename])

    Similar to load, but gets the chunk from file filename or from the standard input, if no file name is given.

    loadstring (string [, chunkname])

    Similar to load, but gets the chunk from the given string.

    To load and run a given string, use the idiom

    assert(loadstring(s))()

    When absent, chunkname defaults to the given string.

    沙盒环境代码:

    --file :testSandEnv.lua
    
    local sandEnv = { os = os , print = print , _VERSION = _VERSION }
    local func = loadstring("print(_VERSION);print(os.execute());" , "loadSandEnv")
    
    setfenv(func,sandEnv)
    
    func()
    

    Usage

    兼容代码:

    -- Run a Lua script in a separate environment
    local function run_script(script, env)
        local env = env or {}
        local func
    
        -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists
        if _G.setfenv then
            func = loadstring(script)
            if func then
                setfenv(func, env)
            end
        else
            func = load(script, nil, nil, env)
        end
    
        if func == nil then
                error("Invalid syntax.")
        end
        func()
    
        return env
    end
    

    总结

    The 5.1 way is sometimes considered simpler and more versatile, but it also requires special treatment of environments (instead of using the existing local variable system). Also, the 5.2 way is designed with the idea that a function's environment is supposed to be private, instead of being accessible from everywhere without the debug library, so it can be considered safer.

    Lua5.1的方法有时候会被认为更加的简单和多功能,但是它也需要对环境做出特殊的处理(替代现有的局部变量系统)。而Lua5.2方法是认为函数的环境应该为私有的理念来设计的,而不是在不需要调试库的情况下从任何地方都可以访问,因此可以认为Lua5.2的方法更加安全。

    REF

    http://lua-users.org/wiki/EnvironmentsTutorial

    http://www.jb51.net/article/55816.htm

    http://blog.csdn.net/xenyinzen/article/details/3485633

  • 相关阅读:
    20170416
    汇总02
    总结
    在编程的世界中,如何高效地学习理论知识,应用理论知识来解决实际生产中的问题
    周末待整理
    web 性能提升
    es6
    http、https、 json、 ajax
    微信小程序 问题收集
    eslint
  • 原文地址:https://www.cnblogs.com/sylvan/p/8597488.html
Copyright © 2011-2022 走看看