Global, Functional and Block Scoping
Variables declared outside of any function are bound to the global scope and can be used pretty much anywhere in your program.
Finally, variables declared with let or const are special cases because they are bound to a block scope instead. This means that these variables can only be used inside the scope they are declared in.
Pitfall: Slower Code Execution in the Browser
The following code snippet takes more time running in Google Chrome than in Node.js, even though both of them are made with the same engine.
Run this command in the browser to see how long it takes. This test was done in the browser “Version 83.0.4103.61 (Official Build) Built on Ubuntu , running on Ubuntu 18.04 (64-bit)”.
The output was,
On the other hand, this test in Node was done in “Node.js v13.14.0.“
The output was,
The output of console.time varied by a few milliseconds every time it was executed but the most representative time taken was chosen.
It’s 20% slower in Google Chrome compared to Node.js.
However, with a simple change of scoping, the code will execute faster and at about the speed in both Google Chrome and Node.js.
The Reason for the Time Difference
The output time will be the same if you paste the snippet, as shown above. This leads to an interesting side-effect. Looking at the window object in your browser, you will find it contains the variable i with the number of times the for loop repeated,
If you open a new tab the variable doesn’t exist,
This side effect is linked to the reason for Google Chrome being slower running the command compared to Node.js.
If we apply our knowledge about scoping to the code snippet, we realize that the scope of the variable i is global since it’s not declared inside any function and it wasn’t declared using let or const.
The browser’s global scope is quite crowded, making the code slower compared to a less crowded one, such as the global Node.js scope.
Using Functional Scoping
You can modify the code to make use of functional scoping by moving the snippet inside a function and then calling it. This way the hoisting of variables will only happen inside that much smaller scope. The results should look like this,
The runtimes are 5 and 4 times lower compared to the original snippet in the browser and Node.js, respectively. And they are more similar between themselves.
This improvement can also be achieved by modifying the original snippet and just changing the declaration of the variable i from var to let.
Pitfall: Unexpected Behavior when Scoping
Scoping also relates to avoiding hard to find issues and avoiding unexpected behaviours. For example, consider the following snippet where two functions are defined but one uses var and the other uses let,
The code snippet declares and assigns a string to a variable. It opens a block scope and redeclares it assigning another value to the variable. The difference in the variable declaration is enough for the output to be different: functional_scoping() output is “inside if” and block_scoping() output is “outside if.”
In the first case, due to functional scoping, the if block doesn’t create a new scope so the variable is reassigned and outputs “inside if”.
In the second case, block scoping applies and the first declaration of the variable is not overwritten, so the output is “outside if”.
As a rule of thumb you should try to avoid declaring your variables using var and instead use const or let to avoid these and more pitfalls that ES6 is meant to solve.
Knowing about scoping and following best practices can help in the long run to avoid frustrating debugging sessions and to write faster code!
If you have any questions you can reach me at [email protected].