Skip to content

Instantly share code, notes, and snippets.

@brainysmurf
Last active June 21, 2024 05:53

Revisions

  1. brainysmurf revised this gist Mar 11, 2020. 1 changed file with 37 additions and 9 deletions.
    46 changes: 37 additions & 9 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,20 @@
    # 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 new V8 runtime in Googel Apps Scripts.
    A bit of a monologue about various syntax changes and adjustments in learning and using the tool.


    ## Logging is slooooooow

    I know. [Everyone knows](https://issuetracker.google.com/issues/149519063).

    That's why I [wrote a library](https://github.com/classroomtechtools/modularLibrariesV8/blob/master/Logger/Logger.js) that writes to a spreadsheet instead. Ever since I started using it I'm far more productive. I don't know how people are still putting with that god-awful lagging logger.

    ## The font of knowledge

    If you want to see the details of changes to the language, in all of its geeky details, [head on over to here](https://github.com/lukehoban/es6features/blob/master/README.md).

    Note that modules are not included, so all that `import` goodness doesn't apply to the V8 runtime.

    ## String literals

    I won't go through normal, everyday usage, but will instead highlight what might be less common but quite handy when needed.
    @@ -195,9 +202,9 @@ The `(_` is confusing at first, but it's actually the `_` representing that ther
    Kinda cool.
    ## Deconstruction
    ## Destructuring
    I think of deconstruction as bulk re-assignment. The syntax is hard to get used to, but it results in values being assigned. It's a bit weird, but here goes:
    I think of destructuring as bulk re-assignment. The syntax is hard to get used to, but it results in values being assigned. It's a bit weird, but here goes:

    ```js
    const bart = 'Bart Simpson';
    @@ -221,15 +228,15 @@ const __log__ = Logger.log;

    I like using `__log__` for this, although you could just as easily use `log`, but `_` can be part of variable names and it's easy to find them again after you've finished debugging.

    Deconstruction is also useful for objects. The syntax follows the same pattern: You can make new variables and assign it values at the same time.
    Destructuring is also useful for objects. The syntax follows the same pattern: You can make new variables and assign it values at the same time.

    ```js
    const obj = {greeting: 'Hello', name: 'World'};
    const {greeting: hello, name: world} = obj;
    Logger.log(hello + world); // "HelloWorld"
    ```

    But why? Isn't using deconstruction with arrays easier to read?
    But why? Isn't using destructuring with arrays easier to read?
    ```js
    const obj = {greeting: 'Hello', name: 'World'};
    @@ -257,7 +264,7 @@ DoSomethingUseful(obj);
    ### Shortcuts
    With deconstruction, if the key and the variable name are identical, you don't have to type it twice. The above could be rewritten like so:
    With destructuring, if the key and the variable name are identical, you don't have to type it twice. The above could be rewritten like so:
    ```js
    function DoSomethingUseful(settings) {
    @@ -322,7 +329,7 @@ UnsophisticatedFunction(name='World', greeting='Hello');
    // ^--- outputs "World Hello" BOOOO
    ```
    That last one is just dumb. But we can use deconstruction to our advantage!
    That last one is just dumb. But we can use destructuring to our advantage!
    ```js
    function SophisticatedFunction({greeting="Hello", name="World"}) {
    @@ -343,15 +350,15 @@ SophisticatedFunction();
    // ^--- "TypeError: Cannot destructure property `greeting` of 'undefined' or 'null'"
    ```
    That's happening because you're passing nothing as a parameter and it's trying to deconstructure on nothing. No sweat, let's change the function declaration to avoid this:
    That's happening because you're passing nothing as a parameter and it's trying to destructuring on nothing. No sweat, let's change the function declaration to avoid this:
    ```js
    function EvenMoreSophisticatedFunction({greeting="Hello", name="World"}={}) {
    Logger.log(greeting + name);
    }
    ```
    The difference is in the `={}` part of the parameter list. If nothing is passed, it makes it an empty object `{}` by default, which makes deconstruction a-ok.
    The difference is in the `={}` part of the parameter list. If nothing is passed, it makes it an empty object `{}` by default, which makes destructuring a-ok.
    You can mix positional parameters and named parameters:
    @@ -476,6 +483,27 @@ A function that is on the class itself might be called a "class method" in other

    What I really like is making instances with the class methods, in what might be called a "convenience method." In the example above, we use the class method to make an instance of the class that contains `"Hello World"` in the state.

    You can use this convenience method pattern to avoid having to use the `new` keyword, which you have to do with classes. Sorta cool:

    ```js
    class App {
    static new () {
    return new App();
    }
    }

    const app = App.new();
    ```
    There are actually a few of these class methods in common use for built-in objects. Do these look familiar?
    ```js
    Object.create({});
    // ^--- makes an duplicate object

    Array.from('Hello World');
    // ^--- makes an array of characters: ['H', 'e' ...]
    ```
    ### Class methods that are also getters
  2. brainysmurf revised this gist Mar 11, 2020. 1 changed file with 149 additions and 16 deletions.
    165 changes: 149 additions & 16 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,12 @@
    # 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.
    A bit of a monologue about various syntax changes and adjustments in learning and using the new V8 runtime in Googel Apps Scripts.

    ## Logging is slooooooow

    I know. [Everyone knows](https://issuetracker.google.com/issues/149519063).

    That's why I [wrote a library](https://github.com/classroomtechtools/modularLibrariesV8/blob/master/Logger/Logger.js) that writes to a spreadsheet instead. Ever since I started using it I'm far more productive.
    That's why I [wrote a library](https://github.com/classroomtechtools/modularLibrariesV8/blob/master/Logger/Logger.js) that writes to a spreadsheet instead. Ever since I started using it I'm far more productive. I don't know how people are still putting with that god-awful lagging logger.

    ## String literals

    @@ -146,7 +146,7 @@ function getResponseFromUrl(url) {
    } catch (e) {
    Logger.log("Oh no, error");
    }
    return response; // fails
    return response; // okay
    }
    ```

    @@ -160,7 +160,7 @@ function getResponseFromUrl(url) {
    Logger.log("Oh no, error");
    }();

    return response; // fails
    return response; // okay
    }
    ```
    @@ -213,7 +213,11 @@ const {log: __log__} = Logger;
    __log__('Hello World'); // same as Logger.log('Hello World');
    ```
    What's happening here? It's assigning the variable `log` to `Logger.log` by evaluating it as an object reference. So clear right?
    What's happening here? It's assigning the variable `log` to `Logger.log` by evaluating it as an object reference. So clear right? I actually think that's kinda awful. Why not just do this?

    ```js
    const __log__ = Logger.log;
    ```

    I like using `__log__` for this, although you could just as easily use `log`, but `_` can be part of variable names and it's easy to find them again after you've finished debugging.

    @@ -225,6 +229,16 @@ const {greeting: hello, name: world} = obj;
    Logger.log(hello + world); // "HelloWorld"
    ```

    But why? Isn't using deconstruction with arrays easier to read?
    ```js
    const obj = {greeting: 'Hello', name: 'World'};
    const [hello, world] = [ obj.greeting, obj.name ];
    Logger.log(hello + world); // "HelloWorld"
    ```
    I think so anyway.
    ### Default values
    If an object doesn't have the key, you can assign default values too. This is best to understand in the context of a function definition as settings:
    @@ -305,7 +319,7 @@ UnsophisticatedFunction(greeting='Hello', name='World');
    // ^--- outputs "Hello World"

    UnsophisticatedFunction(name='World', greeting='Hello');
    // ^--- outputs "World Hello"
    // ^--- outputs "World Hello" BOOOO
    ```
    That last one is just dumb. But we can use deconstruction to our advantage!
    @@ -339,15 +353,31 @@ function EvenMoreSophisticatedFunction({greeting="Hello", name="World"}={}) {
    The difference is in the `={}` part of the parameter list. If nothing is passed, it makes it an empty object `{}` by default, which makes deconstruction a-ok.
    You can mix positional parameters and named parameters:
    ```js
    function Mixed(one='Hello', two='World', {greeting="Hello", name="World"}={}) {
    Logger.log(one, two);
    }
    ```
    But that to me defeats the purpose of named parameters. Exception: If the name of the function gives away what the parameter is doing:
    ```js
    function doSomethingById(id, {greeting="Hello", noun="World"}={}) {
    Logger.log(id);
    }
    ```
    ### How about requiring certain named parameters?
    You may wish a function that is using named parameters to require that a certain name be required to be passed. For example, in our hello world example, ensure that something is passed for `greeting` and `noun`. This is simple to do without named parameters:
    You may wish a function that is using named parameters to require that a certain property be required to be passed. For example, in our hello world example, ensure that something is passed for `greeting` and `noun`. This is simple to do without named parameters:
    ```js
    function HelloWorld (greeting, noun) {
    Logger.log(greeting + noun);
    }
    HelloWorld('Hello') // fails
    HelloWorld('Hello') // fails, which is what we want. yay
    ```
    There is a way, and it borrows the idea of "interfaces" (a concept in other languages) to implement. It's kinda involved, though, which is why I [wrote a library for it](https://gist.github.com/brainysmurf/feee30a117a11e6e4df322d9da935c83).
    @@ -360,17 +390,23 @@ You can! Do it like this:
    ```js
    function MostSophisticated({greeting="Hello", noun="World", ...kwargs}) {

    Logger.log(kwargs); // object
    }
    ```
    No kwargs will not be an array, it will be an object with properties. Yes, because the `...` here just means "remaining."
    ### Nested named parameters?
    Just don't. I'm not going to explain why it doesn't work very well, but it's ugly and not worth it.
    Just don't. I'm not going to explain why it doesn't work very well, but it's very ugly and not worth it.
    ## A class is like a function that creates objects that have functions, oh and state
    ## Classes
    Get to know how to classes in JavaScript. Not only are they useful but they are powerful, and it's likely that others will start using them throughout their own code too.
    ### A class is like a function that creates objects that have functions, oh and state
    Classes allow us to create things that are very expressive and useful. To see how useful they are, let's first just look at how we can play around with functions with them. Let's construct a nonsense `"Hello World"` class.
    @@ -425,7 +461,7 @@ instance.setter = 'Bonjour';
    const value = instance.getter // value now equals "Bonjour"
    ```

    Instead of using them as functions, you use assignment statements.
    Instead of using them as functions, you use assignment statements (or as part of an expression.)

    The final piece of blueprint says that we have a function on the class itself. It says that we have a function called `createHello` which we can invoke by using dot notation on the class variable `App`.

    @@ -436,14 +472,19 @@ const instance = App.createHello();

    In this way, we have created an instance of our little app which will output `"Hello World"` when we use `instance.log()`

    ### Static methods that are also getters
    A function that is on the class itself might be called a "class method" in other languages; the others would be "instance methods." Having this distinction is useful. Instance methods change or retrieve state, or do calculations depending on the state. Whereas class methods are useful when you do something that doesn't depend on state.

    What I really like is making instances with the class methods, in what might be called a "convenience method." In the example above, we use the class method to make an instance of the class that contains `"Hello World"` in the state.


    ### Class methods that are also getters

    I really like this as a design pattern:

    ```js
    class Utils {
    power (base, exponent) {
    return base * exponent;
    return base ** exponent;
    }
    }

    @@ -458,5 +499,97 @@ class App {
    }

    const app = new App();
    const two = App.utils.calc(app.value);
    ```
    const four = App.utils.power(2, 2); // returns 4
    ```
    We'll take advantage of this pattern to solve a confusing change from Rhino to V8 below.
    ## The Global Context
    There is a major change in the global context. It's really confusing, but the change was a necessary one. In Rhino, you could use variables between files that were defined in the global context, and it was all good. I suppose it was done that way in order to make it easier to get started. In V8, that's all broken.
    Why? It has to do with the files that are on the project and how they are parsed. The technical details are boring. What's really interesting is figuring out how to compensate without having to know all the niggly details.
    In the end, though, we'll have a design pattern we can use to our advantage.
    Let's keep it intuitive and learn by rule-making, and so here goes:
    > Any code that is executed **before** the endpoint is called *should not* refer to a function / variable in another file, unless it gets executed **after** the endpoint has been called
    What the heck are you going on about. Let's break it down:
    ### "After the endpoint is called"
    What's the endpoint? In the online editor, that's the function that you click play on. Or it's the `onEdit` function that gets executed as a result of an edit in the spreadsheet. Or it's the function that is exposed via the library mechanism. Just whatever gets called is the endpoint.
    So you click the `run` button with Endpoint selected:
    ```js
    // before the endpoint is called
    function Endpoint() {
    // after the endpoint is called
    }
    // code here is also executed before endpoint is called
    ```
    What about the case with more than one file? Let's see
    ```js
    // Code.gs:
    const before = 'before endpoint';
    function Endpoint () {
    const after = 'after endpoint';
    }
    const afterButBefore = 'before endpoint too';

    // Business.gs
    const stillBefore = 'still before endpoint';
    function NotTheEndpoint () {
    const never = 'never gets here';
    }
    const alsoStillBefore = 'yes even here';
    ```
    So, between those files, we can't really refer to variables or functions between the two (we'll see an exception in a minute).
    Now we know what "the endpoint" is talking about, but also why it's the pivotal thing to think about for the next bit.
    ### Why say "should not" instead of "can not"?
    Because, depending on rules not worth knowing, it might work in some cases. But it has to do with the order in which the files are parsed. So it depends, but let's just nevermind all that. So, I'm being really literal in my rule-making. (Can you tell I'm a programmer?)
    ### What's the illusive exception you referred to?
    Glad you asked. It has to do with classes!
    Check out the following code:
    ```js
    // App.gs
    class App {
    constructor () {}

    get module () {
    return new Module();
    // ^--- does this work?
    }
    }

    // Module.gs
    class Module {
    log (text) {
    Logger.log(text);
    }
    }

    // Code.gs
    function Endpoint () {
    const app = App();
    app.module.log('Hello World');
    }
    ```
    This gets us to the "unless" part of the rule above. By all appearances, you might be thinking that the `new Module()` won't work because it's in the "before the endpoint is called" area. Alas, it is not. That code gets executed well after the endpoint is called, in fact, and in this way you can organize your project accordingly.
    This pattern, which is a form of composition, allows for the creation of files that does some aspec of the application logic, and also allow you the developer to keep various functions organized in files as you'd like them to be.
  3. brainysmurf revised this gist Mar 4, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ A bit of a monologue about various syntax changes and adjustments in learning an

    ## Logging is slooooooow

    I know. [Everyone knows](https://mail.google.com/mail/u/0/#inbox/FMfcgxwGDNSxdKcHMQrbQMdnrmjxCsWS).
    I know. [Everyone knows](https://issuetracker.google.com/issues/149519063).

    That's why I [wrote a library](https://github.com/classroomtechtools/modularLibrariesV8/blob/master/Logger/Logger.js) that writes to a spreadsheet instead. Ever since I started using it I'm far more productive.

  4. brainysmurf revised this gist Feb 29, 2020. 1 changed file with 323 additions and 3 deletions.
    326 changes: 323 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,60 @@

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

    ## Logging is slooooooow

    I know. [Everyone knows](https://mail.google.com/mail/u/0/#inbox/FMfcgxwGDNSxdKcHMQrbQMdnrmjxCsWS).

    That's why I [wrote a library](https://github.com/classroomtechtools/modularLibrariesV8/blob/master/Logger/Logger.js) that writes to a spreadsheet instead. Ever since I started using it I'm far more productive.

    ## String literals

    I won't go through normal, everyday usage, but will instead highlight what might be less common but quite handy when needed.

    ```js
    const noun = 'World';
    const hello = `Hello ${noun}`;
    ```

    I want to do generalize something like this to work in any case:

    ```js
    function output (string, noun) {
    Logger.log( /* `Hello ${noun}` */ )
    }
    function entrypoint () {
    const string = 'Hello ${noun}'; // just a regular string
    output(string, 'World');
    }
    ```

    In other words, use a normal string, pass it some other part of code that can take the `${var}` pattern and apply it there. Why do I want this? Not only because it is cool, but because it allows me to de-couple code. I can define the string template in one place, but apply the template in another.

    The function I use for this ability is here, [which I totally didn't write myself](https://stackoverflow.com/questions/29182244/convert-a-string-to-a-template-string):

    ```js
    function interpolate (targetString, params) {
    const names = Object.keys(params);
    const vals = Object.values(params);
    return new Function(...names, `return \`${targetString}\`;`)(...vals);
    }
    ```

    It is used like so:

    ```js
    const output = interpolate('${greeting} ${noun}', {greeting: 'Hello', noun: 'World'});
    Logger.log(output); // "Hello World"
    ```

    Understanding the mechanics of how `interpolate` works is beyond the scope of this gist, and beyond what you really need to use it (although it's fun once you've groked it).

    In any case, now you can define a template in one place and then apply it in another.



    ###

    ## 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.
    @@ -10,7 +64,7 @@ The main gotcha has to do with the behaviour of the `this` keyword inside one of

    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.
    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. (You do want to use classes, see below.)

    ### Weird syntax thingies with arrow functions

    @@ -100,7 +154,7 @@ That's just fine. But I much prefer this method:

    ```js
    function getResponseFromUrl(url) {
    const response = (_ =>
    const response = (_ => {
    return UrlFetchApp.fetch(url);
    } catch (e) {
    Logger.log("Oh no, error");
    @@ -139,4 +193,270 @@ 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.
    Kinda cool.
    ## Deconstruction
    I think of deconstruction as bulk re-assignment. The syntax is hard to get used to, but it results in values being assigned. It's a bit weird, but here goes:

    ```js
    const bart = 'Bart Simpson';
    const [firstName, lastName] = bart.split(' ');
    firstName; // = 'Bart';
    lastName; // = 'Simpson';
    ```

    That's what it is for array assignments, but you can also do for object assignments. But when it comes to using objects, I think it more as a re-mapping of variables to values.
    ```js
    const {log: __log__} = Logger;
    __log__('Hello World'); // same as Logger.log('Hello World');
    ```
    What's happening here? It's assigning the variable `log` to `Logger.log` by evaluating it as an object reference. So clear right?
    I like using `__log__` for this, although you could just as easily use `log`, but `_` can be part of variable names and it's easy to find them again after you've finished debugging.
    Deconstruction is also useful for objects. The syntax follows the same pattern: You can make new variables and assign it values at the same time.
    ```js
    const obj = {greeting: 'Hello', name: 'World'};
    const {greeting: hello, name: world} = obj;
    Logger.log(hello + world); // "HelloWorld"
    ```
    ### Default values
    If an object doesn't have the key, you can assign default values too. This is best to understand in the context of a function definition as settings:

    ```js
    function DoSomethingUseful(settings) {
    const {verbose=false: verbose, log=true: log} = settings
    if (verbose && log) {
    Logger.log('Hello World');
    }
    }

    const obj = {verbose: true};
    DoSomethingUseful(obj);
    ```
    ### Shortcuts
    With deconstruction, if the key and the variable name are identical, you don't have to type it twice. The above could be rewritten like so:
    ```js
    function DoSomethingUseful(settings) {
    const {verbose=false, log=true} = settings
    // verbose and log variables are defined!

    if (verbose && log) {
    Logger.log('Hello World');
    }
    }

    const obj = {verbose: true};
    DoSomethingUseful(obj);
    ```
    If you think this is cool, check out named parameters, but we're better off spending some some with "spreading" things.
    ## The "spread" ellipsis `...`
    The `...` in code has actually more than one use but shares the same syntax. One thing it does is converts a variable that has a name, say `args` into a list that is populated with assigned values, which I think of as "remaining":
    ```js
    const [one, two, ...remaining] = [1, 2, 3, 4, 5];
    Logger.log(remaining); // [3, 4, 5]
    ```
    Another sort of "remaining" usage, but this time for objects:
    ```js
    const {one, two, ...remaining} = {one: 1, two: 2, three: 3, four: 4};
    Logger.log(remaining); // {three: 3, four: 4}
    ```
    When applying values, that's when I think of it as "spreading."
    ```js
    function add (a, b) {
    return a + b;
    }
    const args = [1, 2];
    add(...args);
    ```
    ## Named parameters for functions
    Full disclosure: I am a Pythonista first and foremost, which means I have very strongly influenced by Python and its ecosystem. This section is a good example of that. I think it is very grand to be able to give names to the parameters instead of by position. So much more readable. So much better.
    So this is cool, but unsophisticated, and we should make it better:
    ```js
    function UnsophisticatedFunction(greeting="Hello", name="World") {
    Logger.log(greeting + name);
    }

    UnsophisticatedFunction('Hello', 'World');
    // ^--- outputs "Hello World"
    UnsophisticatedFunction(greeting='Hello', name='World');
    // ^--- outputs "Hello World"

    UnsophisticatedFunction(name='World', greeting='Hello');
    // ^--- outputs "World Hello"
    ```
    That last one is just dumb. But we can use deconstruction to our advantage!
    ```js
    function SophisticatedFunction({greeting="Hello", name="World"}) {
    Logger.log(greeting + name);
    }

    SophisticatedFunction({greeting='Hello', name='World'});
    // ^--- outputs "Hello World"

    SophisticatedFunction({name='World', greeting='Hello'});
    // ^--- outputs "Hello World"
    ```
    It works much better that way eh? Easier to read eh? What happens if we invoke with no parameters?
    ```js
    SophisticatedFunction();
    // ^--- "TypeError: Cannot destructure property `greeting` of 'undefined' or 'null'"
    ```
    That's happening because you're passing nothing as a parameter and it's trying to deconstructure on nothing. No sweat, let's change the function declaration to avoid this:
    ```js
    function EvenMoreSophisticatedFunction({greeting="Hello", name="World"}={}) {
    Logger.log(greeting + name);
    }
    ```
    The difference is in the `={}` part of the parameter list. If nothing is passed, it makes it an empty object `{}` by default, which makes deconstruction a-ok.
    ### How about requiring certain named parameters?
    You may wish a function that is using named parameters to require that a certain name be required to be passed. For example, in our hello world example, ensure that something is passed for `greeting` and `noun`. This is simple to do without named parameters:
    ```js
    function HelloWorld (greeting, noun) {
    Logger.log(greeting + noun);
    }
    HelloWorld('Hello') // fails
    ```
    There is a way, and it borrows the idea of "interfaces" (a concept in other languages) to implement. It's kinda involved, though, which is why I [wrote a library for it](https://gist.github.com/brainysmurf/feee30a117a11e6e4df322d9da935c83).
    ### But what about keyword arguments (kwargs)?
    What if you want to accept some parameters with default values, but the function can also accept any remaining values, which is then an object?
    You can! Do it like this:
    ```js
    function MostSophisticated({greeting="Hello", noun="World", ...kwargs}) {

    }
    ```
    ### Nested named parameters?
    Just don't. I'm not going to explain why it doesn't work very well, but it's ugly and not worth it.
    ## A class is like a function that creates objects that have functions, oh and state
    Classes allow us to create things that are very expressive and useful. To see how useful they are, let's first just look at how we can play around with functions with them. Let's construct a nonsense `"Hello World"` class.
    ```js
    class App {
    constructor (value) {
    this.value = value;
    }

    log () {
    Logger.log(this.value);
    }

    get getter () {
    return this.value;
    }

    set setter (value) {
    this.value = value;
    }

    static createHello () {
    return new App('Hello World');
    }
    }
    ```
    You can think of a class as a kind of blueprint that declares how an object behaves, and there's a way to create "instances" of these objects built on the blueprint. Or you can think of a class as a function that has a bunch of functions contained inside, and holds state.
    In the above example the only state that it holds is `value`, making it not very useful. State is initialized by the `constructor` function, which is invoked when you call the `App` variable the same way you would a function:

    ```js
    const instance = App('a value, any value');
    // ^--- executes this.value = value;
    ```

    So that class is called like a function, which returns a thing with some functions defined, and state. We can interact with it according to the blueprint:

    ```js
    instance.log();
    // ^--- executes Logger.log(this.value)
    ```

    The blueprint says that "log is a function with no parameters, which can be invoked with `.get`."

    The next two items on the blueprints uses the getter/setter pattern, where `get` and `set` preceded the name of the function. The concept of a getter and setter is that you don't actually use them as a function, though.

    ```js
    instance.setter = 'Bonjour';
    // ^--- executes this.value = 'Bonjour';

    const value = instance.getter // value now equals "Bonjour"
    ```

    Instead of using them as functions, you use assignment statements.

    The final piece of blueprint says that we have a function on the class itself. It says that we have a function called `createHello` which we can invoke by using dot notation on the class variable `App`.

    ```js
    const instance = App.createHello();
    // ^--- executes return new App("Hello World");
    ```

    In this way, we have created an instance of our little app which will output `"Hello World"` when we use `instance.log()`

    ### Static methods that are also getters

    I really like this as a design pattern:

    ```js
    class Utils {
    power (base, exponent) {
    return base * exponent;
    }
    }

    class App {
    constructor () {
    this.value = 1;
    }

    static get utils () {
    return new Utils();
    }
    }

    const app = new App();
    const two = App.utils.calc(app.value);
    ```
  5. brainysmurf created this gist Feb 26, 2020.
    142 changes: 142 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,142 @@
    # 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

    ```js
    // 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:

    ```js
    _ => {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:

    ```js
    _ => ({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`/`let`instead.

    ### 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:

    ```js
    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:

    ```js
    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:

    ```js
    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:
    ```js
    (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:
    ```js
    (text => {
    Logger.log(text)
    })('Hello World');
    ```
    Which means we can get around the earlier `const` problem by doing this:

    ```js
    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.