JavaScript tip 4: use let and const

ES6 (aka. ECMAScript 2015, 6th edition) introduced the let and const statements.

These are meant as alternatives to var and baring browser compatibility issues, I recommend that you should be using them over var.

Speaking of browser compatibility, modern browsers support them, the main pain point being IE version 10 and older. Sadly some of us (ahem… yours truly) still sometimes work on intranet applications that must support older browsers so I mention it.

The difference between let and var is that let is scoped to the current scope (function, block, expression) while var is scoped to it’s containing function or barring that globally.

Here are some examples of behaviour which people not used to var‘s scoping might find odd:

function test1() {
  var foo = 1;

  if (true) {
    var foo = 2;
  }

  console.log(foo);  
}

test1();

function test2() {
  let foo = 1;

  if (true) {
    let foo = 2;
  }

  console.log(foo);  
}

test2();

Here test1() returns 2 even though the second foo declaration was in an inner scope. test2() on the other hand returns 1 has the second foo is scoped to it’s containing if statement.

Here is another example:

function test3() {
  for (var i = 0; i < 10; i++) {
    var inner = i;  
  }
    
  console.log(inner);
}

test3();

function test4() {
  for (let i = 0; i <= 10; i++) {
    let inner = i;  
  }
  
  console.log(inner);
}

test4();

In test3(), the output is 9 which is the last value of i in the loop’s inner scope. test4() returns an inner is not defined error which is more appropriate.

const scoping rules work like let but:

  • You must assign a value at declaration
  • You cannot re-assign a new value later on

This behaviour is similar to readonly and const in other languages like C# and affords an extra level of security for values that cannot change.

JavaScript tip 3: Namespaces

To avoid collisions, declarations overwriting each other and such, we can use JavaScript to create namespaces.

While not supported directly by the language the pattern is pretty common.

First let’s imagine we are including two separate JS files which both include a function named customRounding.

// first included file
var customRounding = function(number) {
  return number.toFixed(2);
}

// second included file
var customRounding = function(num) {
  return num.toFixed(3);
}

In our index.html we are calling this function:

console.log(customRounding(1.23456));

The problem is that the second file will have overwritten the declaration in the first file. To prevent such a collision, it is advisable to use a namespace likewise:

// first included file
var NS1 = {
  customRounding: function(number) {
    return number.toFixed(2);
  }
};

// second included file
var NS2 = {
  customRounding: function(num) {
    return num.toFixed(3);
  }  
};

// in our index.html file
console.log(NS1.customRounding(1.23456));

We can also use this pattern to make some variables and functions private by converting our namespace to an IIFE and then declaring the private members as variables and returning our public members:

var NS1 = (function() {
  // private stuff
  var aPrivateVar = 42;
  var aPrivateFunction = function(number) {
    alert('Hi!');
  };

  // public stuff
  return  {
    customRounding: function(number) {
      return number.toFixed(2);
    }  
  };  
})();

 

 

JavaScript tip 2: Function factories using closures

Here’s how we can use closures to build “function factories”:

var addElement = function(destination) {
  return function(element) {
    document.getElementById(destination)
            .appendChild(element);
  }
};

var addElementToMain = addElement("main");
var addElementToAlternate = addElement("alternate");

var p = document.createElement("p");
p.appendChild(document.createTextNode("Vanilla JS"));

addElementToMain(p);

You can also check out and run this example as a CodePen here.

In this example, addElement is a function which will return a new function with the destination parameter closed. This way we can create functions to add child elements to various parent elements.

The addElementToMain and addElementToAlternate are two functions created this way.

JavaScript tip 1: Function Expressions

If you are creating your JavaScript functions using Function Declarations, a good tip is to switch to Function Expressions.

First here is an example of a Function Declaration:

function declared() {
  console.log("First declared function.");
}

Sadly because of variable hoisting, which means that declarations are hoisted to the top of their scope in JavaScript, we can run into some problems likewise:

function declared() {
  console.log("First declared function.");
}

declared();

function declared() {
  console.log("Overwriting declared function.");
}

In this case since both Function Declarations are hoisted to the top we end up with “Overwriting declared function.” being logged out to the console.

Here’s what the code looks like after hoisting:

function declared() {
  console.log("First declared function.");
}

function declared() {
  console.log("Overwriting declared function.");
}

declared();

Function Expressions provide another way to create a function and avoid this kind of problem. Here’s the previous example using Function Expressions:

var expression = function() {
  console.log("First Function Expression.");
};

expression();

var expression = function() {
  console.log("Not gonna happen!");
};

Which this time is returning “First Function Expression.”. While both expression variable declarations are hoisted up, their definitions aren’t.

Here’s this last example after hoisting:

var expression = undefined;
var expression = undefined;

expression = function() {
  console.log("First Function Expression.");
};

expression();

expression = function() {
  console.log("Not gonna happen!");
};

It’s thus safer to use Function Expressions over Function Declarations.