Category
Software Engineering

Published
Feb 7, 2015

Updated
Feb 9, 2015

Read
7 min

Tweet
Share
Comment

JavaScript Bracket Notation - Practical Examples

Article Purpose

There are numerous examples online and in books that accurately describe the difference between dot notation and bracket notation in JavaScript. What I find lacking however are practical examples of the less common bracket notation. The purpose of this article is to provide practical examples of using bracket notation.

What Bracket Notation Is

Bracket notation enables a developer to access an object’s properties in JavaScript. It is an alternative to the more common dot notation. The detailed breakdown of each notation is littered across the internet; take your pick to learn more:

Practical Examples

Bracket notation is a useful tool. The examples below provide practical applications of its use. If you have improvement ideas or additional application examples, just let me know (@derekknox or derek [at] derekknox.com) and I’ll update the examples.

Succinct Update Listeners

The main idea behind this example is to determine which of two methods to call and call it in a succint and DRY fashion. When reading code, I commonly see an addEventListeners() method and a corresponding removeEventListeners() method when a single combined method could be created using bracket notation. In the updateListener() method below I'm specifically using 'addEventListener' and 'removeEventListener', but you could instead use the 'on' and 'off' methods of jQuery if you were using that library.

This particular approach is abstract enough that it could be leveraged through a Utils class of some kind for all your elements' listeners. Also, it could easily be expanded to take numerous event types and corresponding handlers via arrays.


//element reference
var btn = document.getElementById('btn');

//bracket notation usage
function updateListener(isAdd, element, type, handler) {
  var method = isAdd ? 'addEventListener' : 'removeEventListener';  
  element[method](type, handler);
}

//example handler
function onClick() {
  console.log('Winning');
}

//add a click listener to btn with the onClick event handler
updateListener(true, btn, 'click', onClick);
Variable Method Calls

This example expands on the Succinct Update Listeners example. The main difference is that there are more than two potential methods to call and the logic determining which to call can be random or more complex. The example below communicates the approach through an automated drawing program.


//variable method types, cached length, and drawTool definition
var methods = ['drawArc', 'drawCircle', 'drawRectangle', 'drawStar', 'drawChuckNorris'],
    len = methods.length,
    drawTool = {
      drawArc: function() { /*draws an arc to the canvas at a random position */ },
      drawCircle: function() { /*draws a circle to the canvas at a random position  */ },
      drawRectangle: function() { /*draws a rectangle to the canvas at a random position  */ },
      drawStar: function() { /*draws a star to the canvas at a random position  */ },
      drawChuckNorris: function() { /*draws Chuck Norris to the canvas at a random position  */ }
    };

//bracket notation usage
function draw() {
  var index = Math.floor(Math.random() * len),
      method = methods[index];
  drawTool[method]();
}

//automated drawing
setInterval(draw, 500);
Object Properties Iteration

The implementation of this example is found in many places, but it can specifically be found in libraries like jQuery and in design patterns like the Mixin pattern. The main idea is that a developer may not know (or care) what properties exist on an object, but he/she needs access to them. By leveraging bracket notation, it is possible to access each property without knowing its name. The below example is sourced from Addy Osmani in his description of the Mixin pattern. Addy is creating a method called augment() which allows the properties of one object's prototype to be copied (via bracket notation) to another object's prototype. This is a form of inheritance.


//Extend an existing object with a method from another
function augment( receivingClass, givingClass ) {
 
    //only provide certain methods
    if ( arguments[2] ) {
        for ( var i = 2, len = arguments.length; i < len; i++ ) {
            receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
        }
    }
    //provide all methods
    else {
        for ( var methodName in givingClass.prototype ) {
 
            //check to make sure the receiving class doesn't
            //have a method of the same name as the one currently
            //being processed
            if ( !Object.hasOwnProperty.call(receivingClass.prototype, methodName) ) {
                receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            }
 
            //Alternatively (check prototype chain as well):
            //if ( !receivingClass.prototype[methodName] ) {
            // receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            //}
        }
    }
}
Succinct Cache Check

This example is related to the Object Properties Iteration example above in its bracket notation use. The main idea here is that a developer can succintly gain reference to a previously created object via a cache. If there is no cached object, it is created and then cached. This is an approach that delays the creation of objects until needed (if ever). This approach works best when there is only a single instance of a particular object, but this is not required. The example below demonstrates a drawing program's requestBrush() method that attempts to return a cached brush. If there is no cached brush, one is created JIT and returned. Bracket notation is leveraged instead of an inferior and lengthy if/else chain or switch/case statement.


//brush creation and cache
var brushCreator = { create: function(name) { /*creates brush and updates cache*/ },
                     cache: { 'round': { length: 5, diameter: 1, width: 2 } } };

//request a brush
function requestBrush(type, brushCreator){
    //bracket notation usage
    if(type in brushCreator.cache) {
        return brushCreator.cache[type];
    }
    //not cached, so create and cache
    return brushCreator.create(type);
}

//user interaction leads to a requestBrush call
requestBrush('round', brushCreator); //gets cached 'round' brush

//subsequent requestBrush calls
requestBrush('flat', brushCreator); //not cached so creates and caches 'flat' brush
requestBrush('fan', brushCreator); //not cached so creates and caches 'fan' brush
requestBrush('mop', brushCreator); //not cached so creates and caches 'mop' brush

//updated cache sample
/*'round': { length: 5, diameter: 1, width: 2 },
  'flat':  { length: 4, diameter: 4, width: 4 }, 
  'fan':   { length: 3, diameter: 3, width: 5 },
  'mop':   { length: 2, diameter: 4, width: 4 } */
Other Ideas?

If you have improvement ideas or additional application examples, just let me know (@derekknox or derek [at] derekknox.com) and I’ll update the examples.