Archive for the ‘javascript’ Category

Underscore.js extend vs. jQuery extend

April 2, 2013

Or more succintly: _.extend vs. $.extend.

They both try the same thing and that is copy the keys from a series of objects into the first object. They extend the first object with the properties in the other objects, like this:

    var a = { k0 : 0 },
        b = { k1 : 1, k2 : 2 };
        c = { k3 : 3, k4 : 4 };
    _.extend(a, b, c);

After the extend, a will be:

       k0 : 0,
       k1 : 1,
       k2 : 2,
       k3 : 3,
       k4 : 4

It can be used to extend objects, and it commonly used for “inheritance” : extending one object’s prototype with the properties and functions of another object. Here’s an example from backbone.js, which extends the Model’s prototype:

   _.extend(Model.prototype, Events, { 
             //more implementation here...

One major difference between underscore.js extend and jquery extend is in the way they deal with sub-objects or nested objects. Jquery does a deep copy of sub-objects, while underscore.js does a shallow copy. You need to keep this in mind as problems caused by shallow copies can be hard to troubleshoot.

Have a look at this code:

    var a = {}, b = {},
        c = { k1 : 1,
              kobj : { k2 : 2 }
    _.extend(a, c);
    _.extend(b, c);
    a.kobj.k2 = 10;
    console.log("a.kobj.k2:", a.kobj.k2);
    console.log("c.kobj.k2:", c.kobj.k2);

Because _.extend does a shallow copy, both a.kobj and c.kobj point to the same object. So changing one, affects the other. The code above shows 10 for both a.kobj.k2 and c.kobj.k2.

Replace _.extend with $.extend and the code does a deep copy and the code above displays a.kobj.k2: 10 and c.kobj.k2: 2.



October 19, 2012

I’ve played a bit (a lot!) with backbone.js recently and it’s a great little framework, I love it. It is so easy to input javascript and get spaghetti that backbone.js, although very simple, helps quite a bit. It helps by giving you an (sort of) MVC structure to your code (a backbone!) and a REST-ful API to persist the app to the server.

While I love it, again, it’s very simplistic and recently I discovered one nasty bug – which I tried to convince the developers of backbone.js that it’s a problem, without much success:

The issue is the collections keep a key/value object/dictionary mapping ids to models. It is called _byId. It is used by .get(id) on a collection, but it’s also used for internal features – like detection of duplicate models when you do a collection.add(). It’s fair to say it’s there to optimize the lookup of a model by id in the collection – a very common operation. Conceptually, it can be argued that duplicating the id-to-model relationship data is a recipe for disaster (duplicated in _byId and within the model), but I am not a purist myself, so I don’t have a problem with that.

In backbone.js, the id is supposed to represent the id of the object on the server. So an id is a unique identifiers across all sessions and across clients & server, and it does mean your model is persisted on the server. Contrast this with a cid, which is a client id, just there for the convenience of being able to refer to objects while they’re not persisted (they don’t have an id) and populated always for models.

Now the problem with _byId is the way it gets updated. When a model is saved, the request goes to the server (via ajax / REST api) and the server persists the model and returns the id. Upon receiving the id, backbone.js automatically updates the model with the id. It also uses a trigger/event on the model to update the collection’s _byId. This is still not a problem.

What is a problem is that the user can turn off all events, by doing a save with { silent : true }. No events will be triggered and the _byId collection will not be updated.

Now this is a classic example of having an internal private data structure (optimization in this case): _byId, relying on an external public feature (events) which the user of the API can turn on/off. This is a big problem because it affects the consistency of the internal data and because this error is not detected early and the point of failure is removed from the root cause. The failures you get with this are failures to find models within the collection, failures to detect and prevent duplicates to be added to the collection. Needless to say it is time consuming to troubleshoot problems like these and this is exactly what I found.

In the end, due to this problem not being accepted as a problem, I had to fix it on my side. And what’s worse is that I had to put it on the client side and not in backbone.js – because I wanted to avoid branching off and having problems every time I want to upgrade to a new version. So, I had to update the _byId mapping myself, a very ugly hack and one that is bound to fail if _byId semantics change.

backbone.js folks, if you’re reading this, please reconsider and fix this issue 🙂