Preventing Side Effects in JavaScript
JavaScript is very dynamic these days but I still see a lot of legacy code, whether it be for optimal backward compatibility or simply that the code hasn't been maintained. One of the practices that makes me cringe is coding that creates unwanted side effects. What's a side effect? A piece of code whereby a variable is created and available throughout a scope when it doesn't need to be. Let me show you a few examples and how to avoid these unwanted side effects.
Array.prototype.forEach()
instead of for(var x = ...)
Looping through a JavaScript array was traditionally done via a for()
loop:
var myArray = [1, 2, 3]; for(var x=0, length = myArray.length; x < length; x++) { // ... } // "x" and "length" are side effects
The side effect of this pattern is at minimum the running index, if not the length as well -- they are available within the entire scope. Array
prototype methods like map
, forEach
, and every
allow the developer to avoid these side effects:
[1, 2, 3].forEach(function(item, index, array) { // No side effects! :) });
No "utility" variables need to be created for the looping, thus avoiding side effects. This is called "functional" programming.
Self-Executing Functions
If you haven't read Hiding Your Privates with JavaScript, and you don't know how to keep private variables in JavaScript, take a few minutes to read it. The same pattern provided in that post allows you to avoid side effects via self-executing functions:
// Example from MooTools source... Browser.Request = (function(){ var XMLHTTP = function(){ return new XMLHttpRequest(); }; var MSXML2 = function(){ return new ActiveXObject('MSXML2.XMLHTTP'); }; var MSXML = function(){ return new ActiveXObject('Microsoft.XMLHTTP'); }; return Function.attempt(function(){ XMLHTTP(); return XMLHTTP; }, function(){ MSXML2(); return MSXML2; }, function(){ MSXML(); return MSXML; }); })(); // The three vars are stuck in the self-executing function, they don't "leak" out
The gist is that you can do loads of processing within the self-executing function (a new scope) without allowing variables leaking out -- the only item returned or leaked is the desired return value.
Tightening up your code includes avoiding side effects and JavaScript makes it easy if you follow these basic practices!
This is a good start, but the philosophy of side-effect free programming runs much deeper, and forms the foundation of the functional programming paradigm. To help people understand the philosophical underpinnings, I wrote “The Dao of Immutability”: https://medium.com/javascript-scene/the-dao-of-immutability-9f91a70c88cd
Very informative article, david. I have touched up the unfinished
forEach
function for the reader to continue reading http://gadgets-code.com/javascript-array-prototype-foreach-method-usageNice follow-up, but I would really encourage you to use
console.log()
in such examples instead ofalert()
.Alright, thanks for the advice :)
Hi, thanks for the article. I hope you continue writing on the topic.
I think the utility variables should not be an issue as long as they do not leak out from methods and variables are always initialized before usage. What bothers me more is the length variable which is global.
I’m curious as to why one would define a
length
variable within the for loop for the array iteration when one could just directly access the arraylength
property, since in the example, thelength
value is never modified.Because computing the length could be expensive. You would do it over and over instead of accessing a variable which is cheaper.
:( Javascript doesn’t cache that length computation behind the scenes I guess?
You are right – in this example we would get away with using the
length
property. In case of other languages or data structures it is a good habit to calculate the iteration count beforehand.Btw. I misread the semicolons –
length
variable is not global.What about performance though? Regular
for
loop seems to be more efficient thanforEach
– http://jsperf.com/for-vs-foreach/37 .I’m confused. I thought it was considered bad practice to use
forEach
on an array?What about this one and is there any side effects?
the for construct doesn’t protect or define index in that case: this is implicitly creating var index, which then leaks. Just run that code in the console and check what “index” is afterwards. It’ll have a value, and probably not even one you’d expect.
use
for(index of myArray)
instead ofin
. It still has the side effect butof
does not loop through all properties of the array.I found this article when I was looking for how to manage side-effects/side-causes in JavaScript a few months ago. Recently I have been experimenting with creating Intentions in JavaScript. They act like promises but build a description of what should be done that is only executed when explicitly called. It might be of interest for other people showing up here. https://github.com/CrowdHailer/intention.js
e.g.
to David W. using the anonymous icon for guest user is deceiving of an experienced user. ;)
In my experience using
forEach
almost always leads to side effects…