【问题标题】:Execution order of GS files in a Project项目中GS文件的执行顺序
【发布时间】:2012-10-02 09:38:19
【问题描述】:

我在哪里可以阅读有关 GS 文件执行顺序规则的文档?

为了解决问题,我创建了两个微不足道的对象,每个对象都在自己的文件中。

1_File.gs

var ObjB = new Object();
ObjB.sayName = "[" + ObjA.sayName + "]";

0_File.gs

var ObjA = new Object();
ObjA.sayName = " I'm A ";

诸如...的调用

Logger.log(ObjA.sayName + " : " + ObjB.sayName);

...得到错误...

TypeError: Cannot read property "sayName" from undefined.

如果我将代码从 1_File.gs 移到 0_File.gs 中,反之亦然,则没有错误,并且日志显示正确...

我是A:[我是A]

将 0_File.gs 重命名为 2_File.gs 也不会影响执行顺序,因此我假设执行顺序取决于首先创建哪个文件。

没有“包含”或“导入”的概念可以让我明确执行顺序吗?

【问题讨论】:

    标签: google-apps-script


    【解决方案1】:

    我在哪里可以阅读有关 GS 文件执行顺序规则的文档?

    没有这样的文档,我认为不会在任何时候发布。同样,C++中静态变量的初始化顺序也是未定义的,依赖于编译器/链接器。

    没有“包含”或“导入”的概念可以让我明确执行顺序吗?

    是的,没有“包含”、“导入”甚至“模块”,但有libraries

    还有一个解决方法是使用闭包。贝娄是一个示例代码。通过执行测试功能,日志包含c.d。这个想法是在所有gs 文件中都有一个以init 开头的函数。在这些函数中,所有全局变量都被实例化。匿名闭包在Code.gs 文件实例化期间执行,并调用所有gs 文件的所有“init”函数。

    代码.gs

    var c;
    
    function callAllInits_() {
      var keys = Object.keys(this);
      for (var i = 0; i < keys.length; i++) {
        var funcName = keys[i];
        if (funcName.indexOf("init") == 0) {
          this[funcName].call(this);
        }
      }
    }
    
    (function() {
      callAllInits_();
      c = { value : 'c.' + d.value };
    })();
    
    function test() {
      Logger.log(c.value);
    }
    

    d.gs

    var d;
    
    function initD() {
      d = { value : 'd' };
    };
    

    【讨论】:

    • 兆字节1024! Javascript 是不是非常丰富!我面临一个明显的“中国难题”,然后像你这样的人带来了一个完美的解决方案。非常好。谢谢。
    • @Hasan。是的,JS 是一门非常有趣的语言,并且发现它非常灵活。
    • 正在绞尽脑汁思考如何让它发挥作用,谢谢!该库似乎通过FILES (?) 包含在内,但这已经足够好了。我最终在onOpen 中调用了初始化程序,这似乎也有效。
    • 我认为检查以“init”开头的函数的循环也应该测试typeof this[ funcName ] == "function" ,因为 Object.keys 还包括所有变量。
    【解决方案2】:

    我通过在每个文件中创建一个类并确保每个类都在原始Code.gs(我将其重命名为_init.gs)中实例化来解决这个问题。实例化每个类就像include 的一种形式,并在执行任何操作之前确保一切就绪。

    _init.gs:

    // These instances can now be referred to in all other files
    var Abc  = new _Abc();
    var Menu = new _Menu();
    var Xyz  = new _Xyz();
    var Etc  = new _Etc();
    
    // We need the global context (this) in order to dynamically add functions to it
    Menu.createGlobalFunctions(this);
    
    function onInstall(e) {
      onOpen(e);
    }
    
    function onOpen(e) {
      Menu.build();
    }
    

    类通常是这样的:

    menu.gs:

    function _Menu() {
      this.build = function() {
        ...
      }
    
      ...
    }
    

    【讨论】:

    • 感谢您花时间添加您的解决方案。我已经很久没有看过它了,以至于我想不起它来知道你是否已经在@megabyte1024 的正确答案上有所进步。 (我不再使用谷歌的东西——这太像一只跳蚤,骑在大象背上,附近有一个很好的抓挠柱。)
    • 我使用了类似的方法,尽管与菜单无关。 @megabyte1024;s 回答的优势在于,拥有一个对应于每个 gs 文件的对象(在我看来是一个“模块”)避免了全局名称空间中变量名可能发生的冲突,并且在调用模块,因为函数名称必须以模块名称为前缀,如上面的Menu.build();
    【解决方案3】:

    如果你有不止一层的继承,你需要给init函数命名,如init000Fooinit010Barinit020Baz,然后在执行前按名称对init函数进行排序。这将确保首先评估 init000Foo,然后是 Bar,然后是 Baz

    function callAllInits() {
      var keys = Object.keys(this);
      var inits = new Array();
      for (var i = 0; i < keys.length; i += 1) {
        var funcName = keys[i];
        if (funcName.indexOf("init") == 0) {
          inits.push(funcName);
        }
      }
    
      inits.sort();
      for (var i = 0; i < inits.length; i += 1) {
        // To see init order:
        // Logger.log("Initializing " + inits[i]);
        this[inits[i]].call(this);
      }
    }
    

    【讨论】:

      【解决方案4】:

      其他答案(即不要编写任何引用其他文件中的对象的顶级代码)描述了避免此问题的理想方法。但是,如果你已经写了很多代码并且重写它是不可行的,有一个解决方法:

      Google App Script 似乎按照它们的创建顺序加载代码文件。首先是最旧的文件,然后是下一个,最后是最近创建的文件。这是未选中“按字母顺序排列文件”时在编辑器中显示的顺序。

      因此,如果您按此顺序保存文件:

      • 代码.gs
      • 1_File.gs(取决于 0_File.gs)
      • 0_File.gs

      一个简单的解决方法是制作 1_File.gs 的副本,然后删除原始文件,从而有效地将其移动到列表的末尾。

      1. 单击 1_File.gs 旁边的三角形并选择“制作副本”
        • 代码.gs
        • 1_File.gs
        • 0_File.gs
        • 1_文件复制.gs
      2. 单击 1_File.gs 旁边的三角形并选择“删除”
        • 代码.gs
        • 0_File.gs
        • 1_文件复制.gs
      3. 单击 1_File copy.gs 旁边的三角形并选择“重命名”,然后从末尾删除“副本”。
        • 代码.gs
        • 0_File.gs
        • 1_File.gs

      现在 0_File.gs 在 1_File.gs 之前加载。

      【讨论】:

      • 如果您想检查订单,请在中断时查看调试监视窗口中的“脚本”对象。
      • 看起来他们已经在编辑器中添加了重新排序脚本的选项,这实际上强制了脚本的加载顺序。我还没有在文档中找到任何东西,但对它进行了一些试验,看起来它工作正常。因此,我认为您的解决方案是执行此操作的最明智的方法,并且使用应用程序脚本编辑器中的这一新功能,您无需手动重新创建文件。我不知道当你用扣子推送代码时会发生什么。
      【解决方案5】:

      截至 2021 年 12 月,这对我有效。很可能,其他答案已经过时了。

      您可以轻松解决此问题。当您查看 Web 编辑器的“文件”部分中的脚本时,您会看到它们有一个顺序。文件按照它们在那里出现的顺序进行评估。单击文件名右侧的三个点会弹出一个菜单,可让您向上或向下移动文件。

      【讨论】:

        【解决方案6】:

        Google Apps 脚本中没有这样的顺序。这完全取决于您在哪里声明这些对象以及如何调用您的函数。 您能否解释一下如何以及何时调用 Logger.log() 代码。 另外,你什么时候声明你的对象 objA 和 objB ? 这些将帮助我们提供更好的答案

        【讨论】:

        • 嗨,斯里克。感谢您的关注。 Logger.log() 在原始 code.gs 中。我总共有三个文件,并且只按照 0_File 和 1_File 的顺序播放。兆字节1024非常彻底地回答了我的问题。
        • 确实有订单。如果您在监视窗口中破坏并检查“脚本”对象,您将按照 GAS 看到它们的顺序看到条目。
        【解决方案7】:

        这就是我将如何做到这一点...

        主要

        function include(filename) {
          return ContentService.createTextOutput(filename);
        }
        
        function main() {
          include('Obj A');
          include('Obj B');
          Logger.log(ObjA.sayName + " : " + ObjB.sayName);
        }
        

        对象 A

        var ObjA = new Object();
        ObjA.sayName = " I'm A ";
        

        对象 B

        var ObjB = new Object();
        ObjB.sayName = "[" + ObjA.sayName + "]";
        

        【讨论】:

        • 这似乎不起作用。我试图实现它,但只是得到错误“未定义 ObjA”。检查createTextOutput() 的文档并没有表明可以以这种方式使用该函数。你能解释一下你是如何做到这一点的吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-07
        • 1970-01-01
        • 2010-12-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多