0

Imagine you have one Code.gs file for all your Apps Script code. It has some sort of JavaScript "class" Apple, global and local variables of the class and function that is using them:

function Apple (type) {
    this.type = type;
    this.color = "red";
}

Apple.prototype.getInfo = function() 
{
    return this.color + ' ' + this.type + ' apple';
};

var globalAppleItem = new Apple('global');

function onOpen() {
  var ss = SpreadsheetApp.getActive();
  var items = [
    {name: 'AppleItem test', functionName: 'menuItem1'},
  ];
  ss.addMenu('Custom Menu', items);
}

function menuItem1() {
  var localAppleItem = new Apple('local');
  Logger.log('localAppleItem.getInfo()=' + localAppleItem.getInfo());
  Logger.log('globalAppleItem.color=' + globalAppleItem.color);
  Logger.log('globalAppleItem.getInfo()=' + globalAppleItem.getInfo());
}

This script executes fine:

Script execution errors:
No 

Logger output:
[14-03-10 17:18:29:309 GST] localAppleItem.getInfo()=red local apple
[14-03-10 17:18:29:309 GST] globalAppleItem.color=red
[14-03-10 17:18:29:309 GST] globalAppleItem.getInfo()=red global apple

After some time you decide that moving the Apple class definition to a separate file is a good idea:

Apple.gs:

function Apple (type) {
    this.type = type;
    this.color = "red";
}

Apple.prototype.getInfo = function() 
{
    return this.color + ' ' + this.type + ' apple';
};

Code.gs:

var globalAppleItem = new Apple('global');

function onOpen() {
  var ss = SpreadsheetApp.getActive();
  var items = [
    {name: 'AppleItem test', functionName: 'menuItem1'},
  ];
  ss.addMenu('Custom Menu', items);
}

function menuItem1() {
  var localAppleItem = new Apple('local');
  Logger.log('localAppleItem.getInfo()=' + localAppleItem.getInfo());
  Logger.log('globalAppleItem.color=' + globalAppleItem.color);
  Logger.log('globalAppleItem.getInfo()=' + globalAppleItem.getInfo());
}

But behavior becomes strange:

Script execution errors: TypeError: Cannot find function getInfo in object [object Object]

Logger output: [14-03-10 17:16:08:830 GST] localAppleItem.getInfo()=red local apple [14-03-10 17:16:08:830 GST] globalAppleItem.color=red

By some means Apps Script "doesn't see" getInfo method definition. And at the same time, property color exists and initialized. I spent much time experimenting and searching the Web. Finally I found a solution which I'll provide as an answer to this post. But precise explanation of this behavior is what I haven't got yet and it would be great if someone could provide one.

Roman Me
  • 131
  • 1
  • 6

1 Answers1

0

As described here, there is some .gs files execution order, which is unspecified, but which you must consider when dealing with global variables. Taking into account 2 circumstances:

  1. The constructor function for the global Apple object was definitely called
  2. As described in a great 3 ways to define a JavaScript class article, there is a way of defining a method function directly in the class constructor function

the Apple.gs code was changed to the following:

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = function() 
    {
        return this.color + ' ' + this.type + ' apple';
    };
}

And this fixed the problem:

Script execution errors: No

Logger output:
[14-03-10 17:14:24:428 GST] localAppleItem.getInfo()=red local apple
[14-03-10 17:14:24:429 GST] globalAppleItem.color=red
[14-03-10 17:14:24:429 GST] globalAppleItem.getInfo()=red global apple

Community
  • 1
  • 1
Roman Me
  • 131
  • 1
  • 6
  • Also, you could just have not used a global variable, which is usually a bad idea on Apps Script. Specially global non-trivial initialization, like yours. The best approach in my option (if you need global) is to use lazy initialization. Lastly, you could have moved your global definition to a line after you prototype definition, therefore guaranteeing it has been loaded. – Henrique G. Abreu Mar 12 '14 at 12:59