I am a professional front end web developer based in London, England. The purpose of this blog is to write about issues in the field of web development as well as about open source projects that I am working on.

Blog posts

ES6 Proxy Object

authorRichard Hunter date createdAug 03 2015 02:15 AM
Recently I was playing around with the Javascript Proxy function in Firefox and discovered a rather nasty bug in it. I was unable to find any information about it on Google but after several hours hacking I managed to come up with a solution which I'm going to share here so as to help anyone else who encounters the same problem.

The Proxy function

The Proxy function is a Javascript function that is part of the ES6 proposal. At present it is implemented only in the Firefox browser so it's pretty much at the experimental stage. That said, it's certainly worth experimenting with because it opens up a whole range of possibilities to Javascript programmers. In essence what it allows you to do is create intercept functions for all the operations that you can perform on an object such as getting and setting variables, deleting, enumerating, and many others. An obvious use case would be validation where you could intercept the call to set a property on an object and prevent it if it didn't match a set criteria, but the possibilities of the Proxy function range far beyond this: given that all of the code we write interacts with objects in one way or the other the potential opens up of altering the whole nature of the language. I'm not going to discuss these possibilities in this article but Brendan Eich presents an interesting slide share here

A working example

Firstly I will describe how the Proxy function is supposed to work. Following is a simple example.

//  a plain ol' JS object
var target = {
    name: 'Rich',
    job: 'programmer'
};

//  handler object which contains our various 'traps'
var handler = {
    //  will be called when an attempt is made to set a property of our proxy.
    set: function (target, property, value, receiver) {
        console.log("set:", property, value);
        target[property] = value;
    }
};
//  now create the proxy.
var proxy = new Proxy(target, handler);
//  setting a property on the proxy will result in the set() trap being called.
proxy.name = "Richard";

//  if we try to access the same property on the original object we will find that
    it has been updated accordingly
console.log(target.name);

Hopefully this is straightforwards enough. If not I'd advise checking out the Proxy tutorial on MDN. In fact check it out in any case! The take home message is that the proxy is a kind of facade around the original object which you can use just in exactly the same way as you'd use the original object. The only difference is that every operation on the proxy can be intercepted by a function in the handler object.

A broken example

The proxy seems to be well behaved enough with normal objects, however I started encountering problems when I attempted to proxy an array.


var target = ['apple', 'banana'];

var handler = {
    set: function (target, property, value, receiver) {
        target[property] = value;
    }
};
var proxyArray = new Proxy(target, handler);

//  when we attempt to push into the proxy array
proxyArray.push('pineapple');

//  the original contents of the array have disappeared 
//  and replaced with the new value!
console.log(target); //  ['pineapple']

//  as has the proxy
console.log(proxy[0]);  // 'pineapple'

//  the length property returns the wrong value
console.log(proxy.length); // '0'

//  forEach doesn't work either
proxy.forEach(function() {
    console.log(arguments); //  never called
});

In the above example, when we attempt to push into the proxy array the existing contents of the array are overwritten and the length property is not set correctly. This is certainly not what we want!

Solution

From examining the output of the above code it looked as if the length property was not being set correctly. Following this idea I eventually came up with this solution: adding a get trap to the handler which correctly returns the length of the array to all calling code


var handler = {

    get : function (target, property) {
        if(property == 'length') {
            return target.length;
        } else {
            return target[property];
        }
    }
};

Conclusion

And that's it! As if by magic this seems to fix all the problems previously encountered; We can now push, pull, shift and unshift on the array. We can call foreach and iterate through it. We can slice and dice it anyway we wish (Ok, so maybe there isn't a dice() function on an array!) Hopefully someone will find this useful.

I Would be interested to hear from anyone else who has experimented with the Proxy function and has encountered this or any other problems. Also generally interested in what uses peoples have found for the Proxy function. I'm working on something at the moment which I hope to finish soon and which will be the subject of a future article. Stay tuned for that.