Every once in a while I review different patterns to see if any that I don’t use might come in handy for any work that might be ongoing, that was previously done or to just try my hand at one of them so that I’ve got it in my toolbox for whenever I may need it. Recently I was reviewing at the apply and call methods and found myself reviewing a “decorator” pattern that used apply. I decided to dive into the pattern and am repeating the learning here…

First, lets start with a class called “MyClass”. It contains a hello property and a method called talk. The method prints a message to the screen using the property’s value as part of the message.

What I would like to do is to decorate a “MyClass” instance so that a different message is printed to the console without changing the class itself. To begin I have the class defined below, created an instance of it called “myInstance”, and have called its talk method to get “hello world” logged to the console:

var MyClass = function(){
    this.hello = 'world';
    this.talk = function(){
        console.log('hello ' + this.hello);
    }
}

var myInstance = new MyClass();
    myInstance.talk();  // prints "hello world"

The above code prints “hello world” to the console. What we will do to this example is print something else to the console without modifying the class. We will “decorate” the “myInstance” object so that a different message is printed to the console when talk is called.

See the following:

'use strict';

var MyClass = function(){
    this.hello = 'world';
    this.talk = function(){
        console.log('hello ' + this.hello);
    }
}

var myInstance = new MyClass();

var decorator = {
    hello:'universe',
    decorate:function(func){
        var that = this;
        return function(){
            return func.apply(that,[]);
        }
    }
}

// the undecorated MyClass instance
myInstance.talk(); // prints "hello world"

// here we decorate "myInstance"
decorator.decorate(myInstance.talk)(); // "hello universe" prints instead

// the undecorated MyClass instance
myInstance.talk(); // still prints "hello world"

Lets look at the “decorator” object. Note that it holds the string “universe” within the hello property. What we want to happen is for the decorator.hello property’s value (“universe”) to override the value of myInstance.hello (“world”) so that the desired “hello universe” message is logged to the console. And, do it in such a way as to NOT modify MyClass.

We can do this by using the apply method to pass along the decorator‘s context into myInstance.talk to temporarily replace the context and thus change the value of myInstance.hello from “world” to “universe”. It does this by passing the “that” reference, which is scoped to the wrapping function (decorator.decorate) where it is then mapped to “this”, a reference to the parent object (decorator), where the hello property can be found.

You don’t have to hold the desired over-riding property within the decorator, you could pass along an object as the desired context as well.

var myClass = function(){
    this.hello = 'world';
    this.talk = function(){
        console.log('hello ' + this.hello);
    }
}

var myInstance = new myClass();

var newData = {hello:'universe'};

function decorate(func){
    return function(){
        return func.apply(newData,[]);
    }
}

myInstance.talk(); // prints "hello world"

decorate(myInstance.talk)(); // prints "hello universe"

myInstance.talk(); // prints "hello world"

A word about these lines:

decorator.decorate(myInstance.talk)(); // prints "hello universe"
// and 
decorate(myInstance.talk)(); // prints "hello universe"

Note the addition of the grouping operator at the end – the extra set of parens – as we are returning a function we must execute it which the parens does for us.

We could have just as easily spent some time to write things out like this:

var d1 = decorator.decorate(myInstance.talk);
    d1(); // prints "hello universe"
// and 
var d2 = decorate(myInstance.talk);
    d2(); // prints "hello universe"

… at the expense of a few more characters and a little memory.

Lastly, the apply method takes 2 arguments, the first being the context and the second is an array of attributes. The second is optional and we’re not utilizing it but I’m in the habit of passing along an empty array to remind myself that an array (or array-like object) is an acceptable value for this second argument. At the expense of getting away from the decorator pattern and into how apply works lets take a look at an example where we pass an array of properties to the decorated function.

'use strict';

var myClass = function(){
    this.hello = 'world';
    this.talk = function(sky,grass,brick,dog){
        if (arguments.length !== 0){
            console.log('the sky,grass,brick,dog);
        } else {
            console.log('hello ' + this.hello);
        }
    }
}

var myInstance = new myClass();

var newData = {hello:'universe'};

function decorate(func){
    return function(){
        return func.apply(newData,['blue','green','red','brown']);
    }
}

myInstance.talk(); // prints "hello world"

decorate(myInstance.talk)(); // prints "hello universe blue green red brown"

myInstance.talk(); // prints "hello world"

You can see that the the positions of the strings in the array match up with the expected arguments of “sky,grass,brick,dog” and so are easy to log to the console.