Three JavaScript Anti-Patterns and How To Avoid Them

By Robert Gravelle

In programming, a pattern represents a best practice, while an anti-pattern represents a lesson that has been learned. The term anti-patterns was coined in 1995 by Andrew Koenig in the November C++ Report that year, inspired by the Gang of Four's book Design Patterns. In his report, Koenig describes anti-patterns as a bad solution to a particular problem which resulted in a bad situation occurring, which provide some degree of guidance on how to get out of said situation and get to a satisfactory solution. In today's article, we'll be taking a look at three of the biggest anti-patterns in JavaScript as well as their solutions.

Extending Object

Perhaps the most frowned-upon anti-pattern is the extending of the base Object's prototype. This is considered to be very bad practice mainly because it breaks for in loops. Consider the following:

var obj = {a: "A", b: "B", c: "C", d: "D"};
for (var key in obj) {
   alert(key +': '+obj[key]); //displays "a: A", "b: B", "c: C", "d: D"
}

Object.prototype.e = "E";
for (var key in obj) {
   alert(key +': '+obj[key]); //displays "a: A", "b: B", "c: C", "d: D", "e: E"
}

var obj2 = {a2: "A2", b2: "B2", c2: "C2", d2: "D2"}; 
for (var key in obj2) {
   alert(key +': '+obj2[key]); //displays "a2: A2", "b2: B2", "2c: C2", "d2: D2", "e: E"
}

The e property is now inherited by all objects, whether you want it or not.

The preferred way to add shared object attributes is to create your object first and extend it. I wrote about the Object.create() method just a short time ago. It's supported in all of the newest browsers including IE 9. For compatibility with older browsers, we can supply our own as follows:

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Here's a base Person class that we will extend from later to make subclasses:

function Person(name, sex) {  
  Person.prototype.populationCount++;  
  Person.prototype.getName=function(){ return name };  
  Person.prototype.getSex=function(){ return sex };  
  Person.prototype.setSex=function(newSex){ sex = newSex; };  
  Person.prototype.die=function(){ Person.prototype.populationCount -- ; };
}
Person.prototype.populationCount=0;

var rob = new Person('Rob','male');
var jeanie = new Person('Jeanie','female');
alert(rob.populationCount);  // displays 2

//the following creates a new public property for rob and sets it to 12
rob.populationCount+=10;
alert(rob.populationCount); //displays 12
alert(jeanie.populationCount); //still displays 2

Child.prototype = Object.create(Person.prototype);
Child.prototype.constructor = function Child(name, sex, age) {
    //call the parent constructor
    Person(name, sex);
    Child.prototype.getAge = function() { return age; };
}
var child = new Child('Ralph', 'male', 3);

alert(child.getName()); //displays "Ralph"
alert(child.getAge());  //displays 3

Pollution of the Global Namespace

AC/DC once proclaimed that Rock & Roll ain't noise pollution. Improperly declared variables are a whole other ball game. Polluting the Global namespace makes it much likelier that variables will clash and unwittingly set them to the wrong values. In JavaScript, which lives in a browser sand box, the global namespace is the window object. You don't have to declare a variable using the var keyword; as soon as you start to use it, and the variable hasn't already been defined, it is simply appended to the global namespace. Hence, x = 2; and window.x = 2; are equivalent. Look at what can happen when you define a c counter in a for loop without the var keyword:

function myFunc() { 
  for (c=0; c

Besides always including the var keyword when declaring your variables, another good idea is to include the ECMAScript 5 "use strict" statement at the top of your scripts. While not yet supported by all of the major browsers, it doesn't cost anything to start using it right away.

You can also place it within a function to turn on strict mode only within its limited scope:

function strictFunc(){
  "use strict";
  // code ...
}

You should also minimize the use of global variables - that is those outside of objects or functions - and global functions. At the very least, you should give your global variables names that clearly identify them as your own. Short names using special characters like "$" and "@" are very likely to clash with those of JS libraries.

Create your own namespaces to group common functionality. That will protect a whole group of variables:

var Finance = {};
Finance.INTEREST_RATE = 2.5;
Finance.calcAnnualizedInterest(startVal, endVal) {
  //...
}

Improper Use of Truthy and Falsey Evaluation

JavaScript incorporates the concept of truthy and falsey values. This allows expressions that do not explicitly return true or false can be coerced into a boolean value for evaluation. That process is called implied typecasting. Developers who don't fully grasp the implications of truthy and falsey evaluation tend to perform overblown filtering in their if and loop tests. Take a look at the following statement:

if (testString != undefined && testString != '') {
  //do something
}

It can be safely reduced to:

if (!testString) {
  //do something
}

Values of zero (0), an empty string (""), null, undefined, and NaN are all falsey in JavaScript. Hence the following loop will not execute:

var testval; //this is undefined
while (testval) {
  //do something
}

To avoid confusion caused by implied typecasting always use the === and !== identity operators to check both the values and the type of the expressions you are comparing:

var zero = 0;
if (zero === false) {
  // doesn't execute because zero is 0, not false
}

Conclusion

It's important to not just be knowledgable about best practices, but to also be aware of their alter-ego, the anti-pattern. By doing so, you'll avoid a lot of the common issues that plague inexperienced developers.



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •