-2

In trying to learn more about JavaScript (Google Apps Script flavor), I created an object structure that I think might be useful for a number of projects. My main goals for this little demonstration are:

  • learn the use objects (classes) and inheritance
  • minimize calls to the Spreadsheet Service by summoning a data table once per sheet
  • use the Named Ranges on the spreadsheet to access the data in the table

The code below seems successful, but surely can be improved. A couple of major questions are:

  • Is there a better way to accomplish the inheritance of methods and properties than this?
  • The Spreadsheet() function gets run for each of the Sheet objects, resulting in the environment structure having separate "ss" and "namedRangeList" properties in each of the Sheet objects. Is this normal and expected? Or is there a better approach to avoid this duplication? Or, is JavaScript just recording pointers to a single instance of these objects, so it really doesn't matter that they appear to be duplicated?
  • Because they are common to and the same for all of the Sheets, I had expected "ss" and "namedRangeList" to show up only at the Environment level and therefore available to the Sheets through inheritance rather than duplication.
  • What other changes or approaches would improve my fledgling use and understanding of classes and objects?

Here is a stripped down version of my code that preserves the essence of the structure but leaves out comments, error handling, and other functionality.

function Environment() {
  this.title = 'Car & Driver';
  this.ui = SpreadsheetApp.getUi();
  this.dd = new Drivers();
  this.cc = new Cars();
}

function Spreadsheet() {
  this.ss = SpreadsheetApp.getActiveSpreadsheet();
  
  this.namedRangeList = {};
  var namedRanges = this.ss.getNamedRanges();
  for (var i = 0; i < namedRanges.length; i++) {
    var range = namedRanges[i].getRange();
    this.namedRangeList[namedRanges[i].getName()] = {
      sheet: range.getSheet().getSheetName(),
      row: range.getRow(),
      column: range.getColumn(),
      rowCount: range.getNumRows(),
      columnCount: range.getNumColumns(),
    }
  }
}
Spreadsheet.prototype = Object.create(Environment.prototype);

function Sheet() {
  Spreadsheet.call(this);
  this.sheet = this.ss.getSheetByName(this.sheetName);
  this.data = this.sheet.getDataRange().getValues();
}
Sheet.prototype = Object.create(Spreadsheet.prototype);

function Cars() {
  this.sheetName = 'Cars';
  this.abbreviation = 'cc';
  Sheet.call(this);
}
Cars.prototype = Object.create(Sheet.prototype);

function Drivers() {
  this.sheetName = 'Drivers';
  this.abbreviation = 'dd';
  Sheet.call(this);
}
Drivers.prototype = Object.create(Sheet.prototype);

Sheet.prototype.idxOf = function(namedRange) {
  return (this.namedRangeList[namedRange].rowCount == 1) ?
    this.namedRangeList[namedRange].row - 1 :
    this.namedRangeList[namedRange].column - 1;
}

function test_Environment() {
  var env = new Environment();
  
  env.ui.alert('The third driver is ' +
      env.dd.data[3][env.dd.idxOf('ddFirst')] + ' ' + env.dd.data[3][env.dd.idxOf('ddLast')] + '.');
  
  var tests = [
    ['dd', 2, 'ddLast'    , 'Bailey'  ],
    ['dd', 3, 'ddLicense' , 'pro'     ],
    ['cc', 1, 'ccRadio'   , 122.5     ],
    ['cc', 4, 'ccModel'   , 'Corvette'],
  ];
  tests.forEach(function(t) {
    var v = env[t[0]].data[t[1]][env[t[0]].idxOf(t[2])];
    Logger.log( (v == t[3]) + ': ' + (t[0] == 'dd' ? 'Driver ' : 'Car ') + 
        t[1] + ' ' + t[2].slice(2) + ' is ' + v );
  });
  
  env.ui.alert(env.title + ' is all done');
}
halfer
  • 19,824
  • 17
  • 99
  • 186
  • 3
    For code review questions you should post on [CodeReview](https://codereview.stackexchange.com/). One thing to consider: use [`class` and `extends` syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class). – trincot Jan 16 '20 at 07:57
  • 1
    Although I agree this question is more fir in the Code Review stack, in apps script classes are not implemented yet, so you cannot use that in this case. – Raserhin Jan 16 '20 at 10:51
  • 1
    @trincot those are not supported in Google Apps Script yet. Some users have access to beta environments with v8 but for the rest it is all rhino – tehhowch Jan 16 '20 at 12:02
  • @op you can verify when debugging that your class objects all have different instances of `this.ss` and `this.namedRangeList`. you'll definitely want to use a factory approach so the factory gives all objects a shared spreadsheet reference, and objects on the same sheet a shared sheet reference. (And you'll eventually want logic for moving objects between sheets properly) – tehhowch Jan 16 '20 at 12:07
  • Thank you all for pointing me to [Code Review](https://codereview.stackexchange.com/). I have re-posted my question there. – Dave in Ohio Jan 16 '20 at 14:37

1 Answers1

1

You can take a look at the mozilla page of JavaScript Objects.

Also there are a tons of questions in stack about this issue about inheritance in javascript.

Also as talked in the comments if you want someone to criticize your code take a look at Code Review

Raserhin
  • 2,516
  • 1
  • 10
  • 14
  • Thank you for pointing me to Code Review. I will repost my message there. – Dave in Ohio Jan 16 '20 at 14:21
  • 2
    @DaveinOhio If you post it there, make sure to read their [FAQ on asking questions](https://codereview.meta.stackexchange.com/q/2436/52915) before posting for maximum succes. – Mast Jan 16 '20 at 14:23