Back

Monkey Patching in JavaScript

Monkey Patching in JavaScript

Monkey patching in JavaScript refers to the practice of modifying or extending the behavior of existing objects or functions at runtime. It involves adding, modifying, or overriding properties and methods of an object without altering its original source code. In this article, you will explore monkey patching in JavaScript.

JavaScript is a prototype-based language, meaning objects can inherit properties and methods from other objects. This differs from class-based languages like Java or C++, where objects are created from classes, and inheritance is defined through class hierarchies.

In JavaScript, each object has a link to another object, known as its prototype. The prototype object, in turn, has its own prototype, and so on, forming a prototype chain. When a property or method is accessed on an object, JavaScript first looks for that property or method on the object itself. If it is not found, it looks at the object’s prototype, then at the prototype’s prototype, and so on up the chain until it finds the property or method or reaches an object with a null prototype.

The JavaScript Prototype Chain

This prototypal nature of JavaScript has several implications. It allows for dynamic inheritance, as prototypes are just objects that can be modified at runtime. This means changes to a prototype will be reflected in all objects that inherit from it, which can lead to unintended side effects if not handled carefully.

However, this dynamic nature also enables monkey patching, allowing the modification of an object or its prototype, even after the object has been created.

Monkey Patching Techniques

There are several ways you can perform monkey patching in JavaScript. Some include:

Extending Object Prototypes

Extending object prototypes in JavaScript allows you to add custom methods or properties to built-in JavaScript objects like Object, Array, String, and more.

You can achieve this by calling the prototype property on the object you want to extend and attaching your implementation. For example, to extend the functionality of the String object, you call String.prototype.<your new function>.

Like so:

String.prototype.convertToAscii = function () {
  return this.split("")
    .map((char) => {
      return char.charCodeAt(0);
    })
    .join(" ");
};

const str = "This is a string";

console.log(str.convertToAscii());
//logs 84 104 105 115 32 105 115 32 97 32 115 116 114 105 110 103

The code block above adds a new function to the String object. The added function convertToAscii converts a string into a sequence of ASCII values separated by spaces.

Thus, any string in your code can access the convertToAscii function even though it is not part of the original string methods.

Similarly, this technique would work on all JavaScript objects with a prototype chain.

For example, the Array object:

Array.prototype.logAllMembers = function () {
  for (let i = 0; i < this.length; i++) {
    console.log(this[i]);
  }
};

const arr = [1, 2, 3, 4, 5];

arr.logAllMembers();

The code block above adds a method that logs all the members of an array to the console.

This is how to perform monkey patching by extending the object’s prototype.

Overriding and Extending Existing Methods

Alternatively, you can “extend” or completely “override” an existing method to perform monkey patching. This involves first creating a copy/reference of the original method to allow you to still access the original behavior if needed before modifying the method.

For example, extend the behavior of the Array.map method to log the original array and the mapped array.

const originalMapMethod = Array.prototype.map;

Array.prototype.map = function (callback) {
  const originalArray = this;
  const originalMappedArray = originalMapMethod.call(originalArray, callback);

  console.log("Original array: ", originalArray);

  console.log("Mapped array: ", originalMappedArray);
};

const arr = [1, 2, 3, 4, 5];

arr.map((item) => {
  return item * 2;
});

The code block above would return the results below:

Original array:  [ 1, 2, 3, 4, 5 ]
Mapped array:  [ 2, 4, 6, 8, 10 ]

Pitfalls and Limitations of Monkey Patching

Monkey patching, while a powerful technique in JavaScript, comes with several pitfalls and limitations you should be aware of. Some of them include the following.

Global Scope Pollution

Global scope pollution refers to a situation where too many variables, functions, or objects are introduced into the global scope. This makes it cluttered and can potentially lead to unintended behaviors.

Monkey patching can introduce global scope pollution by adding or modifying properties and methods on built-in prototypes or objects. This can lead to naming conflicts and bugs, especially when using multiple libraries or codebases.

Compatibility Issues

Monkey patching can introduce compatibility issues, especially when dealing with third-party libraries or different JavaScript environments (e.g., browsers, Node.js). Changes to built-in objects may not work consistently across all environments or versions. It’s generally safer to avoid modifying built-in methods unless you have a compelling reason.

Violation of Encapsulation Principles

Monkey patching in JavaScript can violate encapsulation principles, which are fundamental to object-oriented programming. Encapsulation refers to the idea that an object’s internal state should be hidden and only accessible through well-defined interfaces (methods). When you monkey patch an object or class, you often have to access and modify its internal state directly.

Conclusion

Monkey patching is a powerful technique for extending or modifying existing code dynamically. However, it comes with considerations like understanding the prototype chain, avoiding global scope pollution, and preserving encapsulation.

While implementing monkey patching, you must exercise caution, document changes, and thoroughly test patches to mitigate compatibility issues and security risks.

It is also worth noting that alternative approaches like object composition and class inheritance can provide more structured and maintainable solutions.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay