Preventing Side Effects in JavaScript

By  on  

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!

Recent Features

  • By
    7 Essential JavaScript Functions

    I remember the early days of JavaScript where you needed a simple function for just about everything because the browser vendors implemented features differently, and not just edge features, basic features, like addEventListener and attachEvent.  Times have changed but there are still a few functions each developer should...

  • By
    CSS Gradients

    With CSS border-radius, I showed you how CSS can bridge the gap between design and development by adding rounded corners to elements.  CSS gradients are another step in that direction.  Now that CSS gradients are supported in Internet Explorer 8+, Firefox, Safari, and Chrome...

Incredible Demos

Discussion

  1. 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

  2. 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-usage

    • Nice follow-up, but I would really encourage you to use console.log() in such examples instead of alert().

  3. Island

    Alright, thanks for the advice :)

  4. Radek

    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 array length property, since in the example, the length value is never modified.

    • Andreas

      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?

    • Radek

      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.

  5. Anna

    What about performance though? Regular for loop seems to be more efficient than forEachhttp://jsperf.com/for-vs-foreach/37 .

  6. Danik

    I’m confused. I thought it was considered bad practice to use forEach on an array?

  7. What about this one and is there any side effects?

     var myArray = [1, 2, 3];
    for(index in myArray) {
    	// ...
    }
    • Drew

      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.

    • Adam

      use for(index of myArray) instead of in. It still has the side effect but of does not loop through all properties of the array.

  8. 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.

    var getPage = new HTTPAction(function (fetch) {
      return fetch('/users.html').then(function (response) {
        return response.text()
      })
    })
    
    // No externam call is made untill the action is run
    getPage.run(window.fetch).then(function (body) {
      console.log(body)
    })
    
  9. Adam

    to David W. using the anonymous icon for guest user is deceiving of an experienced user. ;)

  10. Worker11811

    In my experience using forEach almost always leads to side effects…

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!