Gordon Koo

Stanford CS '08

LinkedIn 2010-2013

Airbnb 2013-

LinkedIn | Twitter | Github

  • Backbone's Hard Dependency on jQuery

    06 Mar 2012

    There have been a couple of posts on Hacker News recently about reducing page bloat by cutting out jQuery.

    I thought about my homepage, which included Underscore, Backbone, and jQuery. I realized that the features of jQuery that I use could actually be replaced by simple vanilla Javascript. After all, I mainly use jQuery to add and remove classes, and Backbone’s only hard dependency is Underscore, right? I’m not so worried about IE6 users, since I can probably count the number of monthly visitors to my site—one of which is me—on one hand and I doubt that they use legacy browsers. So I decided to do some spring cleaning and get rid of the magic dollar sign.

    As it turns out, Backbone relies on jQuery for quite a lot of its functionality as well. Maybe it was naive of me to think that Backbone implemented its own event delegation when jQuery had already invented that wheel. But let’s take a look at what Backbone actually needs jQuery for.

    For RESTful persistence, history support via Backbone.Router and DOM manipulation with Backbone.View, include json2.js, and either jQuery ( > 1.4.2) or Zepto.

    RESTful persistence

    Backbone models and collections can be synced with the server via fetch, save, and destroy methods. These methods all delegate to the Backbone.sync function, which wraps jQuery’s $.ajax function and calls GET, POST, and DELETE methods for the respective Backbone model persistence methods. No big deal here. The only “model persistence” I really do is make a Posterous API call to fetch my blog posts via JSONP. I can just create a script element, assign the API URL in the script src attribute, and inject it myself.

    History support via Backbone.Router

    A call to Backbone.History.start uses jQuery to bind a popstate or hashchange event listener to the window object. Backbone.History.stop uses jQuery conversely to unbind these event listeners. No problem. I know how to add event listeners on DOM elements: window.addEventListener. Done.

    DOM manipulation with Backbone.View

    Here’s my beef with Backbone. The documentation understates the extent to which Backbone Views depend on jQuery. One of the new features of Backbone is that all Views, in addition to the regular el property which represents the DOM element associated with the View, also get a $el property which holds the jQuery object for that DOM element. That sounds reasonable enough. In the past, I’ve actually just assigned the jQuery object into the el object for convenience, so giving it its own property seems logical.

    Since Backbone claims its only hard dependency is Underscore and that jQuery is only needed for DOM manipulation, I thought that it would check for the existence of jQuery and only create the $el property if it found that jQuery existed on the page. To my surprise, Backbone Views cannot be created without jQuery. The following code results in a Javascript error:

    <html>
      <head>
        <title>My Test Backbone App</title>
      </head>
      <body>
        <div id="test"></div>
        <script src="underscore-min-1.3.1.js"></script>
        <script src="backbone.js"></script>
        <script>
          var TestView = Backbone.View.extend({
            el: document.getElementById('test')
          }),
          test = new TestView();
        </script>
      </body>
    </html>

    All I’m doing here is literally just creating a Backbone View attached to a div element. That’s it. Backbone complains because it blindly tries to set the TestView.$el property without checking if jQuery even exists on the page. The offending function is View.setElement:

    // Change the view's element (`this.el` property), including event
    // re-delegation.
    setElement: function(element, delegate) {
      this.$el = $(element);
      this.el = this.$el[0];
      if (delegate !== false) this.delegateEvents();
      return this;
    }

    Not surprisingly, the first line of setElement will throw an exception with the message “undefined is not a function.”

    You could make the argument that Backbone applications can consist of models, collections, no model persistence, no history support, and ad hoc DOM manipulation instead of Backbone Views. But that’s extremely far-fetched. I have no idea how many Backbone applications include Views, but my guess would be 99% or up. Backbone’s own description of the framework involves separating data from DOM elements by assigning responsibilities to Models and Views, respectively. So if Views cannot exist without jQuery, why does Backbone still claim Underscore as its only hard dependency? At the least, it seems disingenuous.

  • Under the hood of Socket.IO namespaces

    28 Feb 2012

    I am working on a very simple game using NodeJS and Socket.IO. I decided to separate my game and chat communication into two namespaces in Socket.IO to make the logic a bit easier to follow. One problem I encountered was that I wasn’t sure how to broadcast to all connected clients in a specific namespace. If you’re not dealing with namespaces, it’s simple:

    io.sockets.emit(...);

    But what do you do with namespaces? I tried io.sockets.emit(...) as well as io.of('/chat').sockets.emit(), but neither of these attempts were successful. The documentation on the Socket.IO site also provided no clues.

    While debugging my application, I decided to look into the source of Socket.IO. It’s actually pretty interesting to see some of the code behind how Socket.IO is implemented. I’ll go through a small sample of my code and explain what was going on behind the scenes. If you’re simply interested in the solution, feel free to skip to the bottom.

    A very simplified version of some of my server code looks something like the following:

    var express = require('express'),
        app = express.createServer().listen(8000),
        io = require('socket.io').listen(app),
        gameSocket = io.of('/game'),
        chatSocket = io.of('/chat');

    Let’s break this down line by line.

    var express = require('express'),

    Nothing surprising here. I import the Express module, which is the web framework I am using.

    app = express.createServer().listen(8000),

    This line creates my Express web application. Since the focus of this post is Socket.IO, I don’t show the routes that I set up here, but there are plenty of examples at http://www.expressjs.com.

    io = require('socket.io').listen(app),

    There are two ways to create an instance of Socket.IO. The first is to allow it to create a native Node http server for you. If you call the listen() method on it without any arguments, it will default to a server listening on port 80. Otherwise, the server will listen on the port you specify.

    The second way is to pass it an existing server. This is the way in which I’ve created my socket instance in the code example above, and probably the much more common usage of the library. Express is the example web server used in the Socket.IO documentation, but it would be interesting to see if it works just as well with other more obscure Node frameworks.

    So what actually gets stored into the io variable? The call to listen() creates a Manager object, which manages a lot of the interaction with clients, including exchanging handshakes, generating sessionIds, and listening for heartbeats to detect when a client has disconnected. I did not spend too much time looking into these areas, however, since the part of Socket.IO that I was particularly interested in was the creation of namespaces.

    gameSocket = io.of('/game'),
    chatSocket = io.of('/chat');

    This is where things get interesting. Remember that io is a Manager object. The of method takes a single string argument and creates a SocketNamespace object under that string or returns an existing one if it finds one.

    The SocketNamespace object inherits from Node’s EventEmitter. Each client that connects to Socket.IO gets added as an event listener for the particular namespace through which the client is connected. Then, when the SocketNamespace needs to broadcast to all of its clients (or listeners), it emits the event and the clients' event handlers are invoked. That is a 50,000 foot view of how Socket.IO works!

    What happens if Socket.IO is used without any namespaces? If no namespace is specified:

    // io.sockets is a SocketNamespace object
    io.sockets.on('connection', function () {
      ...
    });

    then Socket.IO defaults to the empty string namespace, ''. The following would be equivalent to the previous code snippet:

    // io.of('') is a SocketNamespace object
    io.of('').on('connection', function () {
      ...
    });

    After understanding what objects are being returned, I was able to figure out how to broadcast to all clients in a namespace:

    // for the general namespace:
    io.sockets.emit('newPlayer', playerName);
    // or for a specific namespace:
    io.of('/chat').emit('newPlayer', playerName);

    This has been a very interesting experience in digging through the Socket.IO source. While I barely scratched the surface of this very useful module, the exposure to the source code has been more than enough to solidify my understanding of Socket.IO namespaces.