Hot File

How to Pragmatic Uses of Monkey Patching in JavaScript

View: 143    Dowload: 0   Comment: 0   Post by: hanhga  
Author: none   Category: Javascript   Fields: Other

9 point/1 review File has been tested

Have you ever worked with third-party code that worked well except for one little thing that drove you nuts? Why did the creator forget to remove those pesky console logs? Wouldn’t it be great if that API call could do just one more thing

Introduction

Have you ever worked with third-party code that worked well except for one little thing that drove you nuts? Why did the creator forget to remove those pesky console logs? Wouldn’t it be great if that API call could do just one more thing? If so then you know it can be difficult (or impossible) to get your changes implemented by the maintainer. But what about changing the code yourself? How can you do that if you don’t have the sources and don’t want to host them by yourself? Welcome to a journey into the world of Monkey Patching in JavaScript!

In this article we’ll look at what Monkey Patching is and work through some different examples, using it to change the functionality of a third-party widget to suit our requirements.

What is Monkey Patching?

Monkey Patching (hereafter referred to as MP) is a technique to override, extend or even suppress the default behavior of a code segment without changing its original source code. This is done by replacing the original behavior with a fixed version.

This article will use an existing feedback box widget which displays a simple, slide-able Popup, as seen in the figure below, containing a feedback form.

The source code was modified to include use-cases which act as the MP targets. By target I mean a specific piece of functionality, feature or, at the lowest level, method which we are going to patch.

Another modification I made was to remove the immediately invoked function expression (IIFE) surrounding the code. This was done in order to focus on the techniques of MP.

You can find the whole example, including the monkey patches discussed in this article, as a Plunker.

Isn’t Monkey Patching a Bad Practice?

Let’s get one thing straight before diving into business: Yes, MP is considered a bad practice — so is the evil eval, imperative programming, mutable data structures, two-way binding and so on.

If you use any of those there will likely be a decent-sized group to tell you you’re doing it wrong and should change this or that to fit a better condition. But as always, there are different tools and techniques available which vary in their suitability for certain scenarios. What seems extreme, crazy, or simply bad sometimes may be the last resort for a specific case. Unfortunately, because some practices are seen as bad you won’t even find a lot of articles describing how to do the wrong thing the right way.

The situation described here might be an unnatural one, driven to the extreme with a fake widget, to show what your options are. You, as the reader, then have to decide whether you like what you see or not. If nothing else, after reading this you will have a better understanding in order to argue against MP.

Targets for Monkey Patching

Before we dive into the techniques let us first examine what we’d like to achieve. The modified widget has a few code smells we’d like to work around.

Hardcoded background color

The first of them is a method called toggleError which is supposed to change the background color of an element based on a boolean parameter

FeedbackBox.prototype.toggleError = function(obj, isError) {
  if(isError) {
    obj.css("background-color", "darkgrey");
  } else {
    obj.css("background-color", "");
  }
}

As you can see it sets the background-color property via the jQuery method css. That’s a problem since we’d like to have this specified via stylesheet rules.

Pesky console logs

While developing the widget, a console log was used give the dev hints about what currently is executing. What might be a nice approach during development, for sure is not the nicest thing to do in a production use. As such we need to find a way to strip all those debug statements.

Intercepting ad-server calls

The widget is great, but it has one strange behavior. Each time you initialize the script, it will make a request to a strange ad server and display unnecessary bloat on our page.

FeedbackBox.prototype.init = function() {
  // call to an adserver we'd like to skip
  $.ajax('vendor/service.json', {
    method: 'GET'
  }).then(function(data) {
    console.log("FeedbackBox: AdServer contacted");
  });

  ...

Overwriting a Method

One of the key concepts of MP is to take an existing function and augment it with custom behavior before or after the call to the original code. But calling the original implementation is not always necessary as sometimes you just want to replace it with your custom actions. This approach is ideal to help us to solve the hardcoded background color.

The location where you apply your MP needs to be after the original implementation is loaded and available. Typically you should strive to apply your changes as close as possible to the target, but keep in mind that the implementation of the target might change over time. As for our example, the initialization along with the MPs will go into the file main.js.

Looking at the widget implementation, we can see there is a FeedbackBox object which serves as the widgets root. Later on the toggleError function is implemented on it’s prototype.

function FeedbackBox(elem, options) {
  this.options = options;  
  this.element = elem;  
  this.isOpen = false;
}

FeedbackBox.prototype.toggleError = function(obj, isError) {
  ...
}

Since JavaScript is a dynamic language and its objects can be modified at runtime, what we ultimately will do is simply replace toggleError with our custom method. The only thing to remember is to keep the signature (the name and passed arguments) the same.

FeedbackBox.prototype.toggleError = function(obj, isError) {
  if(isError) {
    obj.addClass("error");
  } else {
    obj.removeClass("error");
  }
};

The new implementation now simply adds an error class to the given element and thus allows us to style the background color via css.

Augmenting a Method

In the previous example, we saw how to overwrite the original implementation by providing our own. Taking care of the console logs on the other hand is supposed to essentially only filter out specific calls and suppress them. The key to success is to inspect the code you’re embedding and try to understand it’s workflow. Typically this is done by firing up the developer console in your browser of choice and peek into the loaded resources, add break points and debug target code parts to get a feel for what it does. This time, though, all you need to do is open up the implementation from the Plunker example calledvendor/jquery.feedBackBox.js in another tab.

By looking at the debug messages we can see that each one of them starts with FeedbackBox:. So an easy way to achieve what we want is to intercept the original call, inspect the provided text to be written and call the original method only if it does not contain the debug hint.

In order to do so lets first store the original console.log into a variable for later use. Then we again override the original one with our custom implementation, which first checks whether the provided attribute text is of type string and if so, whether it contains the substring FeedbackBox:. If so, we’ll simply do nothing, otherwise we’ll execute the original console code by calling its apply method.

Note that this method takes the context as first parameter, that means the object on which the method should be called, and a magical arguments variable. The later is an array of all the arguments originally passed in to the original console log call.

var originalConsoleLog = console.log;
console.log = function(text) {
  if (typeof text === "string" && text.indexOf("FeedbackBox:") === 0) {
    return;
  }

  originalConsoleLog.apply(console, arguments);
}

Intercepting Ajax Calls

Last but not least, let’s see how we can address the issue with the Ad-Server. Let’s look again at the init function of the widget:

$.ajax({
  url: './vendor/a-d-server.json',
  method: 'GET',
  success: function(data) {
    console.log(data);
    console.log("FeedbackBox: AdServer contacted");
  }
});

The first idea might be to open up the browser and search for how to overwrite a jQuery plugin. Depending on how good your search skills are you may or may not find a suitable answer. But lets stop for a moment and think about what actually happens here. No matter what jQuery does with its ajax method, at some time it will eventually create a native XMLHttpRequest.

Let’s see how that works under the hood. The simplest example, found on MDN, shows us this:

var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
  if (xhttp.readyState == 4 && xhttp.status == 200) {
      // Action to be performed when the document is read;
  }
};
xhttp.open("GET", "filename", true);
xhttp.send();

We see that a new instance of XMLHttpRequest is created. It has an onreadystatechange method we actually don’t care about, and then there are the open and send methods. Great. So the idea is to monkey patch the send method and tell it not to execute calls to a specific URL.

var originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(data) {
  if ( URL DOES NOT POINT TO AD SERVER ) {
    return originalSend.apply(this, arguments);
  }

  return false;
};

Well, it turns out you can’t get the targeted URL from the object itself. Crap. So what do we do? We make it available on the object. Looking for the first chance to get hold of the URL, we can see the open method accepts it as the second parameter. In order to make the URL available on the object itself, let’s MP the open method first.

As before, we will store the original open method in a variable for later use. We then overwrite the original with our custom implementation. Since we have the joy of working with JavaScript, which is a dynamic language, we can simply create a new property on the fly and call it _url, which gets set to the value of the passed in argument.

var originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url) {
  this._url = url;
  return originalOpen.apply(this, arguments);
};

Besides that, we call the original open method and do nothing more.

Revisiting our send MP it’s now pretty obvious how to solve the condition check. Here is the modified version:

var originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(data) {
  if (this._url !== "./vendor/a-d-server.json") {
    return originalSend.apply(this, arguments);
  }

  return false;
};

Conclusion

What we saw here is a short introduction to using Monkey Patching to change the behavior of code at runtime. But, more than that, I hope the article was able to give you an idea how you should approach a problem with monkey patches. While the patch itself is often quite simple, it’s the idea of how and where to adjust the code at runtime that’s important.

How to Pragmatic Uses of Monkey Patching in JavaScript

How to Pragmatic Uses of Monkey Patching in JavaScript Posted on 14-09-2016  Have you ever worked with third-party code that worked well except for one little thing that drove you nuts? Why did the creator forget to remove those pesky console logs? Wouldn’t it be great if that API call could do just one more thing 9/10 143

Comment:

To comment you must be logged in members.

Files with category

  • JUnit 5 State Of The Union using java

    View: 275    Download: 0   Comment: 0   Author: none  

    JUnit 5 State Of The Union using java

    Category: Javascript
    Fields: Other

    4.5/1 review
    JUnit 5 has been under development for about 14 months now and the prototype is almost a year old. Time to summarize what happened so far, where the project stands, and where it’s going.

  • Getting Started with Dropwizard using java

    View: 261    Download: 0   Comment: 0   Author: none  

    Getting Started with Dropwizard using java

    Category: Javascript
    Fields: Other

    4.5/1 review
    Dropwizard is a framework for building RESTful web services in Java. In this tutorial we’re going to have a look at how to get started with developing a Dropwizard application by building a new service from scratch.

  • Build Query NULL Value in MySql

    View: 134    Download: 0   Comment: 0   Author: none  

    Build Query NULL Value in MySql

    Category: Javascript
    Fields: Other

    2.5/2 review
    Misunderstanding NULL is common mistake beginners do while writing MySql query. While quering in MySql they compare column name with NULL. In MySql NULL is nothing or in simple word it isUnknown Value so if you use comparison operator for NULL values...

  • Manage Your JavaScript Application State with MobX

    View: 143    Download: 0   Comment: 0   Author: none  

    Manage Your JavaScript Application State with MobX

    Category: Javascript
    Fields: Other

    2.25/2 review
    This article was peer reviewed by Michel Weststrate and Aaron Boyer. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

  • Build Bringing Pages to Life with the Web Animations API

    View: 142    Download: 0   Comment: 0   Author: none  

    Build Bringing Pages to Life with the Web Animations API

    Category: Javascript
    Fields: Other

    4.5/2 review
    This article is by guest author Dudley Storey. SitePoint guest posts aim to bring you engaging content from prominent writers and speakers of the JavaScript community.

  • How to Style Google Custom Search Manually

    View: 110    Download: 0   Comment: 0   Author: none  

    How to Style Google Custom Search Manually

    Category: Javascript
    Fields: Other

    0/0 review
    Website owners very often decide on using Google’s Custom Search Engine (GCSE) for searching through their content instead of using built-in and/or custom search functionality. The reason is simple – it’s much less work, and most often it does the...

  • Test React Components Using Jest

    View: 4724    Download: 0   Comment: 0   Author: none  

    Test React Components Using Jest

    Category: Javascript
    Fields: Other

    4.5/1 review
    This article is by guest author Jack Franklin. SitePoint guest posts aim to bring you engaging content from prominent writers and speakers of the JavaScript community.

  • Programming Question Reverse String without using String function

    View: 257    Download: 0   Comment: 0   Author: none  

    Programming Question Reverse String without using String function

    Category: Javascript
    Fields: Other

    0/0 review
    Write a program to reverse string without using string function. You don’t have to use any in-built string library function. This problem can be solved by multiple approaches. Let’s check it.

 
Newsletter Email

File suggestion for you

File top downloads

logo codetitle
Codetitle.com - library source code to share, download the file to the community
Copyright © 2015. All rights reserved. codetitle.com Develope by Vinagon .Ltd