var, const and let

One of the key things that V8 has sorted out is the scope of variables. Using var to declare variables meant that anything declared within the scope of a function could easily be accidentally overwritten, causing hard to track down errors. ES6 (since it’s commonly known as V8 in Apps Script – I’ll be referring to it as V8 from now on), has added const and let to the variable declaration vocabulary to help prevent these kind of problems.

Legacy Apps Script

Before switching over to V8 (as described in Apps Script v8), let’s look at these snippets

var overwritten inside a block

Don’t do this

myVar is declared outside the scope of the for loop block, and retains its value inside the loop. However, inadvertently assigning a value at (D) – even by redclaring it – overwrites the original external value. At (E) myVar has now been corrupted by things that went on inside the loop. There may be some reason to want to do this, but it’s very definitely a dangerous anti-pattern.

Const not behaving properly

A const in JavaScript is a value that cannot be reassigned a value within the same block. Another variable of the same name might exist elsewhere in the function, but as long as it is in a separate block, it’s not the same variable. Legacy Apps Script supported the const syntax, but didn’t always behave properly. Here’s a  misleading syntax errors reported by legacy apps script

Worse still, here’s const behaving improperly, and not reporting it.

In this case, it happily accepted a reassignment of myVar without complaint. This would have been the correct behavior if it had redclared myVar inside the block as const myVar, and it did correctly preserve the outside myVar value outside the block. Furthermore, this example shows that the i variable controlling the for loop preserves its value (because it’s a var), but ideally it should be undefined at (F)

V8 scopes

Switching over to V8, we can compare the behavior and new capabilities.

Const redclaration

This one didn’t complain in legacy, but now it correctly notices that there’s an attempt to reassign a value to a variable that’s been declared as a const. We still have that leaking variable i at the end of the for loop though, because var has been used to declare it in the loop.

Use let instead of var for control loops

If you do need to write a for loop (I don’t remember the last time I did to be honest though, since array functions array functions are a much better way to iterate and they were available in legacy Apps Script too) , then it’s best to use let rather than var to prevent leakage. Here, i is undefined outside the block and only lives as long as the block does

Closures

When we discuss closures we generally mean the way that a function can be created to encapsulated its ‘lexical state’, meaning the variables it can see when it’s declared. I cover that in a number of articles such as JavaScript closures – how, where and why  , Abstracting services with closuresJavaScript currying and functional programming  and many others but the concept of scope and what it means for closures are very closely aligned. Moving away from var in favour of (mainly) const and (less often) let will help you understand closures more easily.

Inside can see outside const
Above, const myVar is declared both inside and outside an if block. In fact, these variables are unrelated. The ‘inside’ myVar is visible only in the (if) block it’s declared in, whereas the ‘outside’ myVar is visible in every block inside the (function) block it’s declared in. This property is the basis of closure.

Inside can see outside let

let works in the same way

declaration versus reassignment

However, in the case below, there is only one myVar, and the inner block is modifying it. That’s because we didn’t redeclare it at (C), just simply assigned a different value to the myVar already declared at (A)

Applying this to closures

Here’s an example of a simple closure

The function addSmith returns not a result, but another function that’s expecting an argument of firstName. So where does the lastName come from ? Since the function returned by addSmith has lastName defined at a higher level it ‘inherits’ the context it was defined in, and therefore knows the value of lastName.

Let’s take it a little further, parameterizing the last name for the closure

And a little further

Of course this is a fairly daft example, but the idea of closures, and from that ‘currying’ allow you to build a reuse complex workflows with simple, reusable building blocks.

Golden rules

So we’ve seen these 3 things

  • var can be reassigned to and is the same var within a complete function. (note that the global scope is itself a function, so variables declared in the global scope are visible inside all functions)
  • const can be used to have many varibles of the same name, but in difference blocks (function/if/for/switch etc – essentially section of code that is typically enclosed within {…curly brackets…} is a block) . You can’t reassign a value to a const in the same block scope.
  • let is the same as const, but allows reassignment

Here are the rules that I tend to follow, but they are my rules, so they might not work for you.

  • never use var
  • nearly always use const
  • use let if an inner block is being allowed to reassign to a variable at a higher scope
  • always declare a variable at the innermost scope it is needed at
  • Avoid loops – use array functions, or sometimes, recursion
  • Avoid switch – it’s a mess
  • Never put anything in global scope, other than namespaces (Scope and Namespaces)

All these examples still use the function () {} model, but there are better ways to do that in V8, which I’ll address in another article