The difference between var, let and const.
Prior to ES6, a variable was declared using var, as in
Screenshot 2018-10-25 at 15.30.08
However, var is dogged by what’s known as “hoisting”. Consider this
function f() { Logger.log (x); }
Quite rightly, this fails with this error message.
But it’s quite happy with this
function f() { Logger.log (x); var x = 20; }
So even though a variable isn’t yet defined, it doesn’t flag an error. This is not just an Apps Script thing, but inherent in JavaScript. Its why you can call a function before you define it. Referenced variables are hoisted to the beginning of their scope. This kind of thing can cause obscure bugs, so was one of the reasons that const and let were introduced.
const
The purpose of const is to define a value you set only once. In regular JavaScript, if you assign a value to a const more than once in the same block, you’ll get an error.
And in Apps Script, you get a similar message
function f() { const x = 20; const x = 21; }
An in execution, JavaScript also detects an error when you do this
But Apps Script doesn’t complain, even though this is plain wrong.
function f() { const x = 20; x = 21; Logger.log(x); }
And what’s more, the value is the initial one (20), not the last assigned one (21) – which makes the use of const in Apps Script even more error prone than var.
let
Let is to define a block scoped variable whose value is going to change. Here it is in JavaScript, and the final value is 21
(function f() { let x = 20; x = 21; console.log (x); })();
let doesn’t exist in Apps Script so that takes us back to the drawing board, using var.
Block scopes
The main purpose of const and let though is prevent the leakage that occurs when you use var.
This works just fine
for (var i=0; i < 2 ; i++) { Logger.log (i); }
But imagine you copy this bit of code into another function
function f() { var i =20; if (i === 20) { Logger.log ("all is good"); } else { Logger.log ("all is bad"); } }
to make this
function f() { var i =20; for (var i=0; i < 2 ; i++) { Logger.log (i); } if (i === 20) { Logger.log ("all is good"); } else { Logger.log ("all is bad"); } }
You just broke your function.
The purpose of let then is to define a variable for the lifetime of the block it’s in. Since it doesn’t exist in Apps Script, we have to go to JavaScript for this.
(function f() { let i =20; for (let i=0; i < 2 ; i++) { Logger.log (i); } if (i === 20) { Logger.log ("all is good"); } else { Logger.log ("all is bad"); } })(); 0 1 all is good
So what’s going on here is that the ‘i’ in the for loop is a different ‘i’ than the ‘i’ outside it. This is because of the block scoping of ‘let’ as opposed to the function scoping of ‘var’
const inside a block
Although const is about not reassigning a value to the same variable within a block, each iteration of a block is a new day, so it’s perfectly fine to do this. In fact it is emphasising that ‘a’ should only take an initial value for each loop iteration, whereas ‘b’ is supposed to be changed.
(function f() { for (let i=0; i < 2 ; i++) { const a = i +1; let b = a; Logger.log (a); b = b+1; } })(); 1 2
However, Apps Script doesn’t do let, and neither does it deal with const properly, so we’ll have to use var.
function f() { for (var i=0; i < 2 ; i++) { const a = i +1; var b = a; Logger.log (a); b = b+1; } Logger.log (a); } 1 1 1
The result is wrong in multiple ways
‘a’ takes the first value assigned to it (1) and doesn’t get changed by subsequent iterations, yet neither does it complain about ‘a’ being assigned to a constant.
‘a’ leaks outside its block. ‘a’ should be undefined outside the for loop
const in global space to assign to a function
Consider this – it works fine
function f() { const t = function () { return 't'; } Logger.log (t()); }
Now let’s hoist the function declaration into the global namespace.
const t = function () { return 't'; } function f() { Logger.log (t()); }
Conclusion
Avoid using const in Apps Script for now. It just doesn’t work as it should. For now it’s just a half baked version of var.
It looks like Apps Script is using the Rhino JavaScript engine, and judging by what works and what doesn’t, I’d go for v1.7r3. I’ve done an analysis on supported capabilities here – What JavaScript engine is Apps Script running on?
If anyone wants to check, here’s a list of JS supported capabilities by rhino version. http://mozilla.github.io/rhino/compat/engines.html