How to Correctly Wrap a JavaScript Function
Tags: javascript, webdev, function
Published: 2019-08-27 13:48:02

---
title: How to Correctly Wrap a JavaScript Function
published: true
tags: javascript,webdev,function,
canonical_url: https://trackjs.com/blog/how-to-wrap-javascript-functions/
cover_image: https://trackjs.com/assets/images/blog/2019-08-how-to-wrap-javascript-functions/title.png
description: "Wrapping JavaScript functions lets you add common logic to functions you do not control, like native and external functions."
---

Wrapping JavaScript functions lets you add common logic to functions you do not control, like native and external functions. Many JavaScript libraries, like the TrackJS agents, need to wrap external functions to do their work. Adding wrappers allows us to listen for Telemetry, errors, and logs in your code, without you needing to call our API explicitly.

You may want to wrap a function to add instrumentation or temporary debugging logic. You can also change the behavior of an external library without needing to modify the source.

## Basic Function Wrapping

Because JavaScript is wonderfully dynamic, we can wrap a function by simply redefining the function with something new. For example, consider this wrapping of `myFunction`:

```javascript
var originalFunction = myFunction;
window.myFunction = function() { 
  console.log("myFunction is being called!"); 
  originalFunction(); 
}
```

In this trivial example, we wrapped the original `myFunction` and added a logging message. But there is a lot of things we didn’t handle:

- How do we pass function arguments through?
- How do we maintain scope (the value of `this`)?
- How do we get the return value?
- What if an error happens?

To handle these things, we need to get a little more clever in our wrapping.

```javascript
var originalFunction = myFunction;
window.myFunction = function(a, b, c) { 
  /* work before the function is called */ 
  try { 
    var returnValue = originalFunction.call(this, a, b, c);
    /* work after the function is called */ 
    return returnValue; 
  } catch (e) { 
    /* work in case there is an error */ 
    throw e; 
  } 
}
```

Notice that we are not just invoking the function in this example, but `call`-ing it with the value for `this` and arguments `a`, `b`, and `c`. The value of `this` will be passed through from wherever you attach your wrapped function, `Window` in this example.

We also surrounded the whole function in a `try/catch` block so that we can invoke custom logic in the case of an error, rethrow it, or return a default value.

## Advanced Function Wrapping

The basic wrapping example will work 90% of the time, but if you are building shared libraries, like the TrackJS agents, that’s not good enough! To wrap our functions like a pro, there are some edge cases that we should deal with:

- What about undeclared or unknown arguments?
- How do we match the function signature?
- How do we mirror function properties?

```javascript
var originalFunction = myFunction;
window.myFunction = function myFunction(a, b, c) { /* #1 */
  /* work before the function is called */
  try {
    var returnValue = originalFunction.apply(this, arguments); /* #2 */
    /* work after the function is called */
    return returnValue;
  }
  catch (e) {
    /* work in case there is an error */
    throw e;
  }
}
for(var prop in originalFunction) { /* #3 */
  if (originalFunction.hasOwnProperty(prop)) {
    window.myFunction[prop] = originalFunction[prop];
  }
}
```

There are 3 subtle but important changes. First (#1), we named the function. It seems redundant, but user code can check the value of `function.name`, so it’s important to maintain the name when wrapping.

The second change (#2) is in how we called the wrapped function, using `apply` instead of `call`. This allows us to pass through an `arguments` object, which is an [array-like object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) of all the arguments passed to the function at runtime. This allows us to support functions that may have undefined or variable number of arguments.

With `apply`, we don’t need the arguments `a`, `b`, and `c` defined in the function signature. But by continuing to declare the same arguments as the original function, we maintain the function’s [arity](https://en.wikipedia.org/wiki/Arity). That is, `Function.length` returns the number of arguments defined in the signature, and this will mirror the original function.

The final change (#3) copies any user-specified properties from the original function onto our wrapping.

### Limitations

This wrapping is thorough, but there are always limitations in JavaScript. Specifically, it’s difficult to correctly wrap a function with a non-standard prototype, such as an object constructor. This is a use case better solved by inheritance.

In general, changing the prototype of a function _is possible_, but it’s not a good idea. There are serious performance implications and unintended side-effects in manipulating prototypes.

## Respect the Environment

Function wrapping gives you a lot of power to instrument and manipulate the JavaScript environment. You have a responsibility to wield that power wisely. If you’re building function wrappers, be sure to respect the user and the environment you’re operating in. There may be other wrappers in place, other listeners attached for events, and expectations on function APIs. Tread lightly and don’t break external code.

JavaScript breaks a lot, and in unpredictable ways. [TrackJS captures client-side JavaScript errors](https://trackjs.com/) so you can see and respond to errors. Give it a try free, and see how awesome our function wrapping is.