Monkey patching

Recently, David Walsh tweeted that I had schooled him. I received several questions about what I had schooled him in, so I decided to blog about it.

David was trying to change the behavior of a method of a widget on a page, but for all instances of that widget rather than just one instance. What follows is a lesson on monkey patching. I’m sure there are other tutorials out there about it, but this is more for my sanity (and to get David off my back about blogging) than anything else.

Wikipedia defines Monkey patching as “a way to extend or modify the runtime code of dynamic languages (e.g. Smalltalk, JavaScript, Objective-C, Ruby, Perl, Python, Groovy, etc.) without altering the original source code.” For our purposes, we’ll only be looking at JavaScript. Wikipedia also lists four uses for monkey patching:

  • Replace methods/attributes/functions at runtime.
  • Modify/extend behavior of a third-party product without maintaining a private copy of the source code.
  • Apply a patch at runtime to the objects in memory, instead of the source code on disk.
  • Distribute security or behavioral fixes that live alongside the original source code.

In the JavaScript world, monkey patching can be very important if you’re using a third-party library. This is especially true if you are using it from a CDN or are checking it out from a version control system. We’ll use this HTML as our test page and use Dojo from the AOL CDN:

< !DOCTYPE HTML>
<html>
<head>
	<title>A Monkey-patching example</title>
	<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.4.3/dojo/dojo.js"></script>
	<script type="text/javascript">
		dojo.ready(function(){
			dojo.declare("Adder", null, {
				constructor: function(base){
					this._base = base;
				},
 
				add: function(what){
					return this._base + what;
				}
			});
		});
	</script>
</head>
<body>
</body>
</html>

I will assume basic knowledge of JavaScript, object prototype chains, and dojo.declare. The code examples that follow can be pasted into your browser’s JavaScript console (such as Firebug).

Monkey patching object instances

Let’s say you have an Adder instance and you’d like to change the calculation that happens when calling add(). Simply add an add method to the object instance:

var myAdder = new Adder(5);
myAdder.add(5); // returns 10
myAdder.add = function(what){
	return what + 20;
};
myAdder.add(5); // returns 25

That’s all well and good, but how do we perform the original add calculation, yet modify it without rewriting the method completely? Just store the add method to a variable, add an add method to the object instance, and call the stored add method within the new add method. You’ll generally also want to wrap that all in a closure so only the new method can access the old method. Confused yet? Don’t be:

var myAdder2 = new Adder(5);
myAdder2.add(5); // returns 10
(function(){
	var oldAdd = myAdder2.add;
	myAdder2.add = function(what){
		return oldAdd.call(this, what) + 20;
	};
})();
myAdder2.add(5); // returns 30

One note

You might have noticed that I didn’t use the word “overwrite” when talking about monkey patching object instances. This is because object instances look up their properties from their prototype unless explicitly defined on them (like _base). This is what one might call “masking” and is an important concept in JavaScript. When we were monkey patching before, we were actually masking the add method on the Adder prototype (it’s a subtle difference, but important).

Monkey patching an object’s prototype

Great! We can modify individual instances. But let’s say we have 30 Adder instances and we don’t want to modify them all every time. This is where monkey patching the prototype comes in:

var myAdder3 = new Adder(5);
myAdder3.add(5); // returns 10
Adder.prototype.add = function(what){
	return this._base + what + 10;
};
myAdder3.add(5); // returns 20

Here, we’re overwriting (instead of masking) the add method. This applies to instances already created that haven’t had their methods masked (try myAdder.add(5); and myAdder2.add(5);) and future created instances. As we did before, you can also call the originally defined method:

var myAdder4 = new Adder(5);
myAdder4.add(5); // returns 20
(function(){
	var oldAdd = Adder.prototype.add;
	Adder.prototype.add = function(what){
		return oldAdd.call(this, what) + 10;
	};
})();
myAdder4.add(5); // returns 30
myAdder3.add(5); // returns 30

Note how myAdder3 is also affected by this new monkey patch. As you can see, monkey patching is quite simple yet very powerful. Although I used dojo.declare, this can be used to modify any JavaScript object or prototype chain: you can modify a widget’s behavior, add debugging output to a method, or even modify arguments passed to the original function.

Tagged with: , , ,
Posted in Code
  • http://davidwalsh.name David Walsh

    The moments of feeling stupid were well worth the lifetime of gained skill! :)

  • http://www.randomthink.net/ Brian Arnold

    I like the phrasing “Duck punching” over “Monkey patching”. Sounds funnier. :D

    Still though, good stuff. I’ve seen some actions like this in jQuery, nice to see it Dojo-style. :)

    • http://www.reigndropsfall.net/ Bryan Forbes

      As I mentioned in the last sentence, this isn’t limited to Dojo. I should have probably just left out dojo.declare and done it with basic JavaScript object building. Oh well, I’ll remember that for next time ;) .

  • Pingback: Monkey Patching Dojo's Menu Dijit

  • http://brhdesigns.blogspot.com Bobby

    For MP’ing object instances where you saved the original method in oldAdd, couldn’t you have just called the prototyped method?

    (function(){
    myAdder.add = function(what){
    return Adder.prototype.add.call(this, what) + 20;
    };
    })();
    myAdder.add(5);

    Seems to work without adding a new variable.

    • http://www.reigndropsfall.net/ Bryan Forbes

      You’re right, however there are a few things to think about:

      First, if you save off the prototype method and something monkey patches afterwards, you get the monkey patched version of the method which you might not be expecting.

      Second, each time you call myAdder.add with your version, the interpreter has to look up the global Adder object, then it has to look up its prototype object, and then its add function (which is just an object), and finally it has to find and call its call function. This can be a lot of work for such a simple method depending on how many times the method will be called.

      Third, with the introduction of a closure you use a bit more memory. Combined with my previous point, this will be something you have to decide: speed or memory use.

      By the way, if you use your way you won’t need the function declaration and execution around your masking since you’re not saving anything off.