Extending, Currying and Monkey Patching. part 3

Monkey Patching

Or sometimes known as Duck Punching.

So what we’ve got here is an arbitrary object kimsObject (line 01) thrown in the BINARYMIST global, with a public method kimsMethod (line 03).
The trace method is passed an object and a method name (line 10). It replaces the specified
method with a new method that “wraps” additional functionality around the original method.
The call is made from line 23

var BINARYMIST = {};
BINARYMIST.kimsObject = (function () {
  return {
    kimsMethod: function () {
      alert("Now inside kimsMethod.");
    }
  };
}());

BINARYMIST.trace = (function () {
  return function (targetObject, targetMethod) {
    var originalMethod = targetObject[targetMethod];
    var that = this;
    (function () {
      console.log(new Date(), "Entering:", targetMethod);
      var result = originalMethod.apply(that, arguments);
      console.log(new Date(), "Exiting:", targetMethod);
      return result;
    }(/*execute me!*/));
  };
}());

(function () {
  BINARYMIST.trace(BINARYMIST.kimsObject, 'kimsMethod')
}());

I believe if used with care, and in moderation.
Monkey Patching can support and provide implementation for
Dr. Barbara Liskov’s Liskov Substitution Principle (LSP)

Consider the developer that uses a function he/she thinks does something, but it actually does something entirely unexpected.
Be sure to make known to the team and those what will use your code what you have done, and what they can expect.
Make sure this is documented in code around the API, whether it be with comments or simply in the way you write your code.

monkey patching

Jeff Atwood has a good post on the dangers of using Monkey Patching.
Be sure to check it out.

You may or may not need the Object.create function. It’s part of the ECMA Standard 262
which is the vendor-neutral standard for what was originally Netscape’s JavaScript.
This allows us to specify the object we want to be the prototype for our new object, I.E. our base class.
Properties added to an object‘s prototype are shared, through inheritance, by all objects sharing the prototype.
Alternatively, a new object may be created with an explicitly specified
prototype by using the Object.create built-in function.

This also makes me think of what we try to achieve with Aspect Orientation in .NET.
Below I’ve had an attempt at trying to make the BINARYMIST.createAspectedObjectWithTracing as generic as possible.
So that on the last line you can instantiate a BINARYMIST.kimsObject and call the kimsMethod method.
The way I’ve setup the BINARYMIST.createAspectedObjectWithTracing, you should be able to instantiate any object and call which ever method on it that you desire.
You get the tracing functionality for free.
Using this sort of approach, you should be able to apply any functionality you want to any method you want.

I’m keen on hearing how this could be improved.
Making the call even more transparent.

BINARYMIST.kimsObject = (function () {
  return {
    kimsMethod: function () {
      alert("Now inside kimsMethod.");
    }
  };
}());

BINARYMIST.trace = (function () {
  return function (targetObject, targetMethod) {
    var originalMethod = targetObject[targetMethod];
    var that = this;
    (function () {
      console.log(new Date(), "Entering:", targetMethod);
      // We can pass any number of args to the original method we wanted to call.
      // We also handle any return value. So hopefully this should be able to be used for any method.
      var result = originalMethod.apply(that, arguments);
      console.log(new Date(), "Exiting:", targetMethod);
      return result;
    }(/*execute me!*/));
  };
}());

// Add create method to the Object function
// Gives us dynamic control of what a base class should look like.
// method (create) that takes an object argument, returns a new object that has the parameter assigned to it's prototype.
// Creates a function on Object (create) that takes an object argument.
// Creates a function (Func) and assigns the object parameter to the function definition's prototype.
// Instantiates and assigns the new function (Func) to the "create" function definition on Object.
(function () {
  if (typeof Object.create !== 'function') {
    Object.create = function (o) {
      var Func = function () {};
      Func.prototype = o;
      return new Func();
    };
  }
}());

BINARYMIST.createAspectedObjectWithTracing = (function (anyObject, anyFunction) {
  var localObject = Object.create(anyObject);
  localObject[anyFunction] = function () {
    return BINARYMIST.trace(anyObject, anyFunction);
  };
  return localObject;
});

// Instantiate your object (any object) and call a method on it.
BINARYMIST.createAspectedObjectWithTracing(BINARYMIST.kimsObject, 'kimsMethod').kimsMethod();

Now as you can see again, all of the objects I’ve defined are sitting tidily out of the global object.

javascript global abatement

Now as you can see below, our BINARYMIST.kimsObject is now part of the new object’s (that localObject references) prototype object.
and it’s method is clearly visible.

javascript prototype

On line 42 above, we set the localObject‘s kimsMethod  to reference a wrapped version of BINARYMIST.kimsObject.kimsMethod.
The wrapped version has the tracing added.

We then return the localObject which references BINARYMIST.kimsObject and call the most specific kimsMethod
which has the functionality we just set.

return BINARYMIST.trace(anyObject, anyFunction);

and as you can see below, we’re now ready to start playing with our target method (kimsMethod).

javascript tracing

We then assign the this. We then execute the  anonymous closure.
In doing so, this is bound to the global object.
So to access the outer scope we need the that.
We then apply the originalMethod to that (BINARYMIST in our case).

Tags: , , ,

2 Responses to “Extending, Currying and Monkey Patching. part 3”

  1. binarymist Says:

    JavaScript Weekly also sent this link out:
    http://javascriptweekly.us1.list-manage1.com/track/click?u=0618f6a79d6bb9675f313ceb2&id=9681652715&e=3531157141

  2. JavaScript Object Creation Patterns | Binarymist Says:

    […] https://blog.binarymist.net/2012/05/27/extending-currying-and-monkey-patching-part-3/ […]

Leave a comment