Google Script 中的全局变量(电子表格)

IT技术 javascript variables google-apps-script global-variables
2021-02-12 10:50:48

我一直试图在 Google 应用程序脚本(电子表格内)中获取一些函数来修改全局变量,但我似乎无法弄清楚。

基本上我想声明一个变量(在本例中为“globalTestVar”),并且每次两个函数(globalVarTestFunctionOne 和两个)中的一个启动该变量时,该变量都应增加 1。

问题是每次按下按钮时都会再次声明该变量,即使 if(typeof(globalTestVar) == 'undefined') 语句应该解决这个问题。

我习惯了 Objective C 和 Java,我可以在开始时声明我的变量,并在代码中的任何地方修改这些变量。

如果这是一个基本问题,我很抱歉,但我已经用谷歌搜索了几个小时,但我无法让它发挥作用。

这是代码:

logstuff("outside");


function logstuff(logInput){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var lastRow = sheet.getLastRow() + 1;
sheet.getRange("A"+lastRow).setValue(logInput);
return;
}


if (typeof(globalTestVar) == 'undefined') {
logstuff('declaring global variable');
globalTestVar = 0;

} else {
logstuff('global variable has been declared'); 
}



function globalVarTestUIFunction() {
var app = UiApp.createApplication().setTitle('Test UI');
var doc = SpreadsheetApp.getActive();
var formPanel = app.createVerticalPanel();


var buttonF1 = app.createButton('F1');
var buttonbuttonF1ClickHandler = app.createServerClickHandler("globalVarTestFunctionOne");
buttonF1.addClickHandler(buttonbuttonF1ClickHandler);
buttonbuttonF1ClickHandler.addCallbackElement(formPanel);

var buttonF2 = app.createButton('F2');
var buttonbuttonF2ClickHandler = app.createServerClickHandler("globalVarTestFunctionTwo");
buttonF2.addClickHandler(buttonbuttonF2ClickHandler);
buttonbuttonF2ClickHandler.addCallbackElement(formPanel);


app.add(formPanel);

formPanel.add(buttonF1);
formPanel.add(buttonF2);


doc.show(app);

return app;
}



function globalVarTestFunctionOne() {
logstuff('globalVarTestFunctionOne');
globalTestVar++;
logstuff('Value of globalTestVar: ' + globalTestVar);
}

function globalVarTestFunctionTwo() {
logstuff('globalVarTestFunctionTwo');
globalTestVar++;
logstuff('Value of globalTestVar: ' + globalTestVar);
}

输出:

  • 外面3
  • 声明全局变量
  • 外面3
  • 声明全局变量
  • globalVarTestFunctionOne
  • globalTestVar 的值:1
  • 外面3
  • 声明全局变量
  • globalVarTestFunctionTwo
  • globalTestVar 的值:1

我已经编写了自己的函数“logstuff”来打印消息,因为我不喜欢内置的 Logger.log 函数。

谢谢!

3个回答

你不会喜欢这样:GAS 中的全局变量是静态的——你不能更新它们并期望它们保留它们的值。我也用谷歌搜索了几个小时。

您可以使用CacheServiceScriptDB尽可能存储此类问题。CacheService快速且易于使用,但由于缓存最终会过期而受到限制。我没试过 ScriptDB

是的,cacheService 适用于字符串和数字,但是可以使用 cacheService 存储数组吗?
2021-03-17 10:50:48
嗯,这很令人不安。必须有一种方法来使用某种全局变量。我想了一个解决方法并使用“变量”表来保存/修改和获取全局变量。但这不是很优雅,也不是很有效。
2021-03-23 10:50:48
伙计们,这不是正在发生的事情。全局变量不是static,它们是global,指的是它们的作用域。在执行上下文中,全局变量可用于所有代码块。当您按下客户端脚本(UiApp、HtmlService)中的按钮时,会创建一个新的服务器端上下文来运行适当的处​​理程序——并且该新上下文具有一组唯一的“全局”变量。严格来说,您的变量没有被“重新声明”,与“未定义”的比较仅意味着它没有在此上下文中分配值
2021-04-11 10:50:48
不 - 您不能在缓存中存储数组,更糟糕的是:JSON.stringify()似乎没有返回 JSON 字符串,也没有报告错误。不过,您可以在 ScriptDB 中存储数组。这是否比将它们作为范围存储在工作表中更好取决于您对它们做什么。
2021-04-12 10:50:48
我曾经CacheService做我需要做的事情,但那是为了提高性能而进行的短期存储。你可能会发现ScriptDB更好,但我还没有尝试过。
2021-04-14 10:50:48

属性服务 -> 属性

目前(2015 年),我认为要走的路是使用类PropertiesService和返回类型Properties

关于全局范围

据我了解,脚本函数的每次新调用(时间触发器、用户单击菜单项、按下按钮等)都会导致对脚本进行新的完整解析,而没有对早期执行的记忆,除非它们以某种方式持久化(在电子表格范围内或使用Properties例如)。

尽管 CacheService 可以工作,但它的最长生命周期为 6 小时。正如@consideRatio 提到的,这可以通过使用 PropertiesService 来解决。

示例包装器可能是(将变量注入全局上下文)

/* Wrap variable inside global context */
const Globals = {
  global:this,
  items:{},
  /* Can be 'User', 'Script', or 'Document'
  ** Script - same values for all executions of this script
  ** User - same values for all executions by same user
  ** Document - same values for any user for same document
  ** Check reference for details.
  ** https://developers.google.com/apps-script/guides/properties
  **
  */
  context:'Script', 
  /* Get property service based on requested context */
  get service() {
    return PropertiesService['get' + this.context + 'Properties']()
  },
  /* Assign implementation */
  set(name, value = null) {
    this.service.setProperty(name, JSON.stringify(value));
    return value;
  },
  /* Read implementation */
  get(name) {
    var value = this.service.getProperty(name);
    return value !== null? JSON.parse(value) : null;
  },
  /* Shortcut for setter of complex objects */
  save(name) {
    this.set(name, this.items[name]);
  },
  /* Save all */
  flush(name) {
    Object.keys(this.items).map(name => this.save(name));
  },
  /* Delete implementation */
  reset(name) {
    this.service.deleteProperty(name);
    delete this.items[name];
  },
  /* Add to global scope */
  init(name, default_value = null) {
    if(! this.items.hasOwnProperty(name)) {
      if(this.service.getProperty(name) === null)
        this.set(name, default_value);
      this.items[name] = this.get(name);
      Object.defineProperty(this.global, name, {
        get: () => {return this.items[name]},
        set: (value) => {return this.items[name] = this.set(name, value)},
      })
    }
    return this.items[name];
  }
}

注册到 Globals.init 后,变量就可以像普通的 var 一样使用了。然而,这适用于原语,因为复杂对象不支持观察者,它们必须在脚本结束时或显式刷新。

/* In case you need to start over */
function restart_simulations() {
  Globals.reset('counter');
  Globals.reset('state');
  
  test_run();
}

function test_run() {
  /* After running init once, you can use global var as simple variable */
  Globals.init('counter', 1); // Required to use "counter" var directly, as simple variable
  
  /* Complex objects are also accepted */
  Globals.init('state', { logined: false, items: [] }); 
  
  /* Using primitives is simple */
  Logger.log('Counter was ' + counter);
  counter = counter + 1;
  Logger.log('Counter is now ' + counter);

  /* Let's modify complex object */
  Logger.log('State was ' + JSON.stringify(state));
  
  state.items.push(state.logined ? 'foo' : 'bar');
  state.logined = ! state.logined;
  
  Logger.log('State is now ' + JSON.stringify(state));
  
  /* Unfortunately, watchers aren't supported. Non-primitives have to be flushed */
  /* Either explicitly */
  //Globals.save('state');  
  
  /* Or all-at-once, e.g. on script end */
  Globals.flush();  
}

这是在不同的 3 次运行中保留的内容

First run:

[20-10-29 06:13:17:463 EET] Counter was 1
[20-10-29 06:13:17:518 EET] Counter is now 2
[20-10-29 06:13:17:520 EET] State was {"logined":false,"items":[]}
[20-10-29 06:13:17:523 EET] State is now {"logined":true,"items":["bar"]}

Second run:

[20-10-29 06:13:43:162 EET] Counter was 2
[20-10-29 06:13:43:215 EET] Counter is now 3
[20-10-29 06:13:43:217 EET] State was {"logined":true,"items":["bar"]}
[20-10-29 06:13:43:218 EET] State is now {"logined":false,"items":["bar","foo"]}

Third run:

[20-10-29 06:14:22:817 EET] Counter was 3
[20-10-29 06:14:22:951 EET] Counter is now 4
[20-10-29 06:14:22:953 EET] State was {"logined":false,"items":["bar","foo"]}
[20-10-29 06:14:22:956 EET] State is now {"logined":true,"items":["bar","foo","bar"]}

您可以在此处查看工作示例。

演示脚本