如何在避免权限错误的情况下使用全局变量?

How to use global variables while avoiding permission errors?

看下面的例子,

function doSomething1(){/*needs ss*/const ss = SpreadsheetApp.openById(/*SPREADSHEET_ID*/);}
function doSomething2(){/*needs ss*/const ss = SpreadsheetApp.openById(/*SPREADSHEET_ID*/);}
function doItAll(){
  doSomething1();
  doSomething2();
}

与其在两个函数中都调用 Spreadsheet,还不如使用全局变量来简化为

const ss = SpreadsheetApp.openById(/*SPREADSHEET_ID*/);
function doSomething1(){/*do something with ss*/}
function doSomething2(){/*do something with ss*/}
function doItAll(){
  doSomething1();
  doSomething2();
}

这里的问题可以在不使用全局变量的情况下通过简单地在函数之间传递ss变量来解决。但是,如果需要访问 ss 变量的多个函数,这将变得更加复杂。而且传递 ss 很麻烦。没有多少方法可以避免 Apps 脚本中的全局变量。不支持模块。如果您使用 IIFE,所有函数都对 IDE 隐藏 - 使得从 IDE 或其他任何地方调用函数都是不可能的。在这里使用全局要优雅得多。但是如果我有一个简单的触发器就会出现问题:

const ss = SpreadsheetApp.openById(/*SPREADSHEET_ID*/);
function doSomething1(){/*do something with ss*/}
function doSomething2(){/*do something with ss*/}
function doItAll(){
  doSomething1();
  doSomething2();
}
function onOpen(){/*Adds a menu*/}

菜单添加 onOpen 将失败,因为此行在 onOpen 之前加载 SpreadsheetApp.openById(/*SPREADSHEET_ID*/) 并且该行需要 permissions/authorizations 而 onOpen 是一个简单的触发器运行 没有任何需要授权的代码。

如何在不出现授权错误的情况下声明全局变量?运行

这个问题可以通过使用 MDN 中提到的 getter. A getter executes the code only when called from anywhere, thus encapsulating the execution of the code in global context. But the getter will execute on each call to the variable. If ss is called in two functions, SpreadsheetApp.openById is executed twice. We can avoid this using lazy loading technique 来解决。

const config = {
  get ss() {
    delete this.ss;
    return (this.ss = SpreadsheetApp.openById(/*SPREADSHEET_ID*/));
  },
};
function doSomething1(){/*do something with config.ss*/}
function doSomething2(){/*do something with config.ss*/}
function doItAll(){
  doSomething1();
  doSomething2();
}
function onOpen(){/*Adds a menu*/}

在这里,我们在对象内部使用了一个getter,而不是直接声明ss。以这种方式使用,SpreadsheetApp.openById() 永远不会在全局范围内调用,尽管它是在全局范围内声明的。它仅在执行 doSomething1 时加载。此外,从 doSomething2 访问时不会再次调用该方法,因为 getter 在第一次访问时被删除并替换为实际值。

虽然代码变得有点笨重,但是这样解决了很多问题,也优雅多了。

样本:

我发现您不必在此处使用 return return (this.ss = SpreadsheetApp.openById(/*SPREADSHEET_ID*/)

有一天我在搞这个。我以这种方式尝试过,它可以像真正的全局变量一样在函数调用之间维护状态,而无需通过启动属性服务的额外步骤。但是,它确实要求您每次都必须 运行 readInit。

 let gobj={get init(){delete this.init;this.globals=getGlobals();this.init=PropertiesService.getScriptProperties().getProperties();}};
     readInit();
    
    function readInit() {
      gobj.init;
      console.log(JSON.stringify(gobj.globals));
      console.log(JSON.stringify(gobj.init))
    }

只是想知道您的想法,这是对不利方面的另一种说法。我还添加了 getGlobals,这是我经常在代码中访问的内容。直到现在我才能用 gobj.globals.key 等访问它 我还没有尝试过任何大的东西所以它可能会减慢速度。