Home > Software engineering >  Avoid Repeating: Use Global Variables in Google Apps Script or Not?
Avoid Repeating: Use Global Variables in Google Apps Script or Not?

Time:11-25

As of 2021, with V8 engine, I'm wondering if it's a good idea to use Global Variables in Google Apps Script? And if it is, how to use them? Is my way a good way (described below)?

Now I checked, of course, all other similar questions. But there are still some details I couldn't find:
Basically what I tried to do is to not repeat the code: I have multiple functions, and I'm storing the active spreadsheet and the current sheet like so:

const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();

enter image description here which leads to

  1. repeating (1)
  2. wasting resources - instantiating spreadsheet and sheet (2)
  3. Increasing variables names inconsistency (ss / spreadsheet / spreadSheet - when copy/pasting from a snippet on the web) (3)

Right?

So I thought of using the Global Variables (GV) whenever I have common local variables in multiple functions.

However, since they’re will be unnecessarily allocated on every function call (there are also other functions that don't need the GVs), I tried to define them only when needed (only when there's function call that uses them) - by not using a defining keyword (var, const, let): enter image description here

According to this link, it seems to be a good approach (pattern 1).

Anyway, I'm wondering if there are any other considerations or downsides I'm not aware of? Is it really a good idea to go this road? Because so far, I didn’t see any snippet that implements this, and I saw a lot of GAS snippets.

One downside I'm aware of is the lack of autocompletion in the new GAS editor, for my GVs (since I didn't define them using 'var' or 'let' to set their scope Global on purpose).

Otherwise, I'm aware of PropertiesService and CacheService. However I'm willing to reuse my script (where I defined my GVs) as a library for my other scripts.
Plus, you can only store values as strings in PropertiesService and CacheService (and not SpreadsheetApp.getActiveSpreadsheet()) right? Not to mention that I don't need persistency after the script execution.
So I'm also hesitant to use them instead of GVs.

CodePudding user response:

  • You can use the lazy loading technique in my answer
  • To make it dynamic and avoid repetition, You can use enclosing arrow functions(()=>{}) to avoid direct execution and use Object.defineProperty() to add a getter.
const g = {};//global object
const addGetter_ = (name, value, obj = g) => {
  Object.defineProperty(obj, name, {
    enumerable: true,
    configurable: true,
    get() {
      delete this[name];
      return (this[name] = value());
    },
  });
  return obj;
};

//MY GLOBAL VARIABLES in g
[
  ['ss', () => SpreadsheetApp.getActive()],
  ['MasterSheet', () => g.ss.getSheetByName('Sheet1')],
  ['MasterRangeColA1_5', () => g.MasterSheet.getRange('A1:A5')],
  ['MasterRangeColAValues', () => g.MasterRangeColA1_5.getValues()],
].forEach(([n, v]) => addGetter_(n, v));

const test = () => {
  console.info('start');
  console.log({ g });
  console.info('Accessing MasterSheet');
  console.log(g.MasterSheet);
  console.log({ g }); //note ss is loaded as well
  console.info('Accessing MasterRangeColAValues');
  console.log(g.MasterRangeColAValues);
  console.log({ g }); //note MasterRangeColA1_5 is loaded as well
};

Instead of a global object g, we can also use the global this, in which case, all variables directly become members of a global object:

const addGetter_ = (name, value, obj = this) => {
  Object.defineProperty(obj, name, {
    enumerable: true,
    configurable: true,
    get() {
      delete this[name];
      return (this[name] = value());
    },
  });
  return obj;
};
[
  ['ss', () => SpreadsheetApp.getActive()],
  ['MasterSheet', () => ss.getSheetByName('Sheet1')],
  ['MasterRangeColA1_5', () => MasterSheet.getRange('A1:A5')],
  ['MasterRangeColAValues', () => MasterRangeColA1_5.getValues()],
].forEach(([n, v]) => addGetter_(n, v));

const test = () => {
  console.info('start');
  console.log(this);
  console.info('Accessing MasterSheet');
  console.log(MasterSheet);
  console.log(this); //note ss is loaded as well
  console.info('Accessing MasterRangeColAValues');
  console.log(MasterRangeColAValues);
  console.log(this); //note MasterRangeColA1_5 is loaded as well
};
  • Advantage: You don't have to prefix variables with g. But, global space is polluted.
  • Related