Monday, May 22, 2017

Javascript - Two properties to keep in mind when using block-scoped variables



let and const have some interesting properties other than their operating scope, this post discusses two of these that might save you some head-scratching.

They don't add properties to the global object

Unlike var, even in the global scope, block-scoped variables do not attach properties to the global object.

This might lead to something like this.

Code snippets in this post use node as their execution environment, replace "global" with "window" for the browser.

const c = "constantC";

console.log(global.c); // undefined

global.c = "globalC";

console.log(c); // Prints "constantC"
console.log(global.c); // Prints "globalC"

So, as you can see on line 7, whenever you refer to the variable name, Javascript gives you the const value. If you want the global property you reference it from the global object.

This can lead to the following weird scenario

v = "globalV";

console.log(v); // Prints "globalV"

const v = "constV"; // No problems at all

console.log(v); // Prints "constV"

console.log(global.v); // Still "globalV"

You can indeed, in the global scope, declare a const/let variable with the same name as a global variable (memory leak warning here).

As you know, var behavior is totally different in these scenarios.

A use case

imagine this..

You're stuck with a project with some messed up dependency injection, so one module assumes the other module is in the global scope and uses it directly (talking about old school IIFE mostly)..

When wanting to test that module in isolation is it often a pain to do so, you want to mock/ stub/ fake/ double/ whatever that module.

using the above property can make you set a tempoary global variable for use by the first module, then comes the real one and declare itself as a const in the global scope with no problems.

This, however will not help you re-order tests or run them in parallel, so it's always best to architect the code in the right way (obvious, but not always as easy as it sounds).


They have a temporal "dead zone"

Block-scoped variables will not hoist (In the way we're used to, anyway) to the top of the block, causing a temporal dead zone between the top of the block and variable initialization.

when a Var is defined in code, it is automatically initializaed with undefined, block-scoped variables do not follow the same behavior, which means that declaration and initialization phases are decoupled, which in turn causes an error thrown when trying to access the variable anywhere above its initialization line.

console.log(letV); // Throws an error: letV is not defined

let letV = 2;

A use case

Imagine you want to do something if some object is available to use, like if exports is present in the global scope, then attach members to it.

var exports = exports || null;
if (exports) {
  exports.thing = myThing;
}

This is something you can't do with a const or let without throwing an error.


That's it.

Hope that helped. Please share your thoughts in the comments.