Skip to content

Instantly share code, notes, and snippets.

@brainysmurf
Last active June 21, 2024 05:53
Show Gist options
  • Save brainysmurf/35c901bae6e33a52e3abcea720a6b515 to your computer and use it in GitHub Desktop.
Save brainysmurf/35c901bae6e33a52e3abcea720a6b515 to your computer and use it in GitHub Desktop.
Things one can get used to for the V8 Google Apps Scripts engine

Things one can get used to for the V8 Google Apps Scripts engine

A bit of a monologue about various syntax changes and adjustments in learning and using the tool.

Arrow functions are nice, but learn what happens with this

Arrow functions are fun and dandy, but there are some serious side effects around, and if you're not aware of them can make debugging very frustrating.

The main gotcha has to do with the behaviour of the this keyword inside one of these babies. For long-form function myFunction() {} function, inside the body the this is referring to itself, which is different from what this is outside of the function.

For an arrow function, the this value is the same both inside and outside of it.

Upshot, if you aren't using this anywhere, you don't need to know much about it, but then you won't be able to use classes.

Weird syntax thingies with arrow functions

Weird syntax thingy #1: No parameters

You have two choices when an arrow function does not have any parameters:

  • Empty parentheses
  • Use the underscore _ variable
// empty parentheses:
() => {}
// underscore variable
_ => {}

I prefer the latter, because Javascript has enough parentheses as it is, and it usually hurts my eyes whenever I see it.

And it should be noted that using a single _ is no special meaning in JavaScript: It is actually a legal variable name. In many languages, the _ is used to refer to a placeholder variable that is never used. So in this case, we are actually defining an arrow function with one parameter; it is just never used.

Weird syntax thingy #2:

Arrow functions can optionally have a {} block, in which case you don't have a return statement. But if you return an object, we have a confusing syntax:

_ => {obj: 'obj'}

That looks a lot like a body {} rather than an object literal. So, you're supposed to use parentheses (told you there are a lot of parentheses) instead:

_ => ({obj: 'obj'})

Sigh.

Use const or let instead of var

There is nothing on its own wrong with var, but I think it's worth "overcompensating" when transitioning to V8 and to exclusively use const or let instead for a while, to get the hang of it.

The massive difference between const/let and var is the scope. The former obeys its scoping rules according to the nearest curly braces block, and the latter obeys its scoping rules to the enclosing function.

Any variable made with var has an additional oddity in that it is "hoisted" -- and odd it is. If you haven't learned what hoisted is referring to but have been scripting happily along without knowing much about it, then that is a case in point to use const/letinstead.

When to use const

Whenever you make a variable that will not be reassigned to some value. It is important to understand that if the value itself is an object where assignments make sense (such as objects) you can still use assignment statements on those, just not on the variable immediately after the const.

When to use let

Whenever you make a variable that could be reassigned to some value. It doesn't have to be reassigned, but if there is no possibility of it, use const instead.

const with try/catch

There is an interesting pattern that can be used which I quite like. Here's the problem:

function getResponseFromUrl(url) {
    try {
       const response = UrlFetchApp.fetch(url); 
    } catch (e) {
        Logger.log("Oh no, error");
    }
    return response;  // fails
}

What's the problem? Well, const response is defined inside the try {} area, and is used again in the return statement outside of it. You can't do that, because const variables obey rules according to the immediate curly braces.

You could do this:

function getResponseFromUrl(url) {
    let response;
    try {
       response = UrlFetchApp.fetch(url); 
    } catch (e) {
        Logger.log("Oh no, error");
    }
    return response;  // fails
}

That's just fine. But I much prefer this method:

function getResponseFromUrl(url) {
    const response = (_ =>
       return UrlFetchApp.fetch(url); 
    } catch (e) {
        Logger.log("Oh no, error");
    }();
    
    return response;  // fails
}

What the heck is going on there?

First of all one thing we have is a self-invoking function. You can do that:

(function log (text) {
    Logger.log(text);
})("Hello World");

That calls the function log immediately after being created, with the value "Hello World"as text. The arrow function equivalent is:

(text => {
    Logger.log(text)
})('Hello World');

Which means we can get around the earlier const problem by doing this:

const variable = (_ {
    return 'value';
})();
Logger.log(variable);

The (_ is confusing at first, but it's actually the _ representing that there is no parameter, with a ( enclosing the function until ), there it then calls itself with ().

Kinda cool.

@imthenachoman
Copy link

Great writeup. I think you're missing a { in function getResponseFromUrl(url) { right after the =>.

@imthenachoman
Copy link

Also, do you know the "Everyone knows" link takes you to an email conversation/thread in your email? It won't work for other users...

@bennettscience
Copy link

bennettscience commented Mar 3, 2020

Also, do you know the "Everyone knows" link takes you to an email conversation/thread in your email? It won't work for other users...

I was about to leave the same comment 😀

Your class examples are twisting my brain a little bit. I like the Python-ish syntax and design patterns...have you tried any inheritance with class-based apps script?

@brainysmurf
Copy link
Author

Thanks for pointing out the dumb link thingie. Fixed now.

have you tried any inheritance with class-based apps script?

Yes. My VerboseErrors tool uses inheritance to override the default behaviour of errors.

@zesertebe
Copy link

and..... private methods? :C

@Newbclharri
Copy link

Thanks for the post! I've been having a problem with classes and I'm not sure if it's my logical, gs file arrangement, or if the is just unexpected behavior with classes in the google V8 engine, but I cannot figure out why this code won't run. It's a google apps script project attached to a spreadsheet:

(https://docs.google.com/spreadsheets/d/120mXQ5v4XtBeUQGAv57WkXuFhZBGwn6mywEY5vGU_Nw/edit?usp=sharing)

For context, I am trying to understand dependency injection and coupling. I created a class, "TocSheet.gs" that manages a table of contents, and of course relies on Spreadsheet App for functionality as well as PropertiesService to store the state of a TocSheet instance. Any insight would be greatly appreciated, the link is available to the public. To access the code, click Extensions > Apps Script. Thanks for any help you can offer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment