Intercepting method calls via ES6 Proxies

[2015-10-07] esnext, dev, javascript, js proxies
(Ad, please don’t block)

Updated version of this blog post: Tracing method calls via Proxies


This blog post explains how to use ES6 Proxies to intercept method calls to an object.

Read chapter “Meta programming with proxies” in “Exploring ES6” for more information on Proxies.

The problem  

You can intercept the operation get (getting property values) via a proxy and you can intercept the operation apply (calling a function), but there is no single operation for method calls that you could intercept. That’s because method calls are viewed as two separate operations: First a get to retrieve a function, then an apply to call that function.

The solution  

If you want to intercept method calls, you must therefore intercept get and return a function that intercepts the function call. The following code (which works in Firefox) demonstrates how that is done.

function traceMethodCalls(obj) {
    let handler = {
        get(target, propKey, receiver) {
            const origMethod = target[propKey];
            return function (...args) {
                let result = origMethod.apply(this, args);
                console.log(propKey + JSON.stringify(args)
                    + ' -> ' + JSON.stringify(result));
                return result;
            };
        }
    };
    return new Proxy(obj, handler);
}

I’m not using a Proxy for the latter task, I’m simply wrapping the original method with a function.

Let’s use the following object to try out traceMethodCalls():

let obj = {
    multiply(x, y) {
        return x * y;
    },
    squared(x) {
        return this.multiply(x, x);
    },
};

tracedObj is a traced version of obj. The first line after each method call is the output of console.log(), the second line is the result of the method call.

> let tracedObj = traceMethodCalls(obj);
> tracedObj.multiply(2,7)
multiply[2,7] -> 14
14
> tracedObj.squared(9)
multiply[9,9] -> 81
squared[9] -> 81
81

The nice thing is that even the call this.multiply() that is made inside obj.squared() is traced. That’s because this keeps referring to the proxy.

This is not a very efficient solution. One could, for example, cache methods. Furthermore, Proxies themselves have an impact on performance.