Building Backbone Plugins: Eliminate The Boilerplate In Backbone.js Apps (2014)
PART 1: BACKBONE VIEWS
Chapter 2: Naming And Namespacing Your Plugin
JavaScript has the notion of “global” variables and objects within a browser. Every object, variable or function that is defined in the browser’s JavaScript runtime lives in the global scope by default. This is not a big deal with very small applications. But when an application begins to grow, and begins to use plugins such as jQuery, Underscore and Backbone, it becomes very important to not “pollute” the global scope with too many objects.
The risk of too many global objects or variables is that they will have the same name as another part of the project or another library. How many projects have a config object or method, for example? If they all used the same config object in the global scope, nothing would work together as they would be clobbering each other’s global object.
Building namespaced code is one way to avoid polluting the global space, and most popular libraries do this in some way. Backbone provides the Backbone namespace, for example. The View, Model, and Collection types all hang off of this namespace. And the BaseView that I just created should follow suit. It should be namespaced to either my application or the name of the plugin that I am building.
Unfortunately, JavaScript doesn’t have a formal concept of namespaces, like Java, C#, Ruby and other languages do. But that doesn’t mean we can’t use a namespace pattern to facilitate the same functionality. In fact, there are many options for making this work, including:
· Object literal namespace objects
· The Revealing Module pattern
· … and more variations
Any object can be used as a namespace. All I need to do is assign one object to an attribute of another object, and I effectively have a namespace. This can be a dangerous thing to do, though, as I may accidentally override existing data or functionality on an exsiting object. Instead, it is a better idea to use an empty object literal as the namespace. This way, I am less likely to override something important.
To create an object literal as a namespace, just create an object literal and then assign objects as attributes. For example, the BaseView can be attached to a BBPlug namespace (short-hand for “Building Backbone Plugins”) like this:
1 var BBPLug = {};
2
3 BBPlug.BaseView = Backbone.View.extend({
4
5 render: function(){
6 var data;
7
8 if (this.serializeData){
9 data = this.serializeData();
10 };
11
12 var renderedHtml = _.template(this.template, data);
13 this.$el.html(renderedHtml);
14 }
15 });
To extend from this object, I must specify the entire path to the object, BBPlug.BaseView:
1 HelloWorldView = BBPlug.BaseView.extend({
2 template: "<h1>Hello world!</h1>"
3 });
4
5 DataDrivenView = BBPlug.BaseView.extend({
6 template: "<h1><%= message %></h1>",
7
8 serializeData: function(){
9 return this.model.toJSON();
10 }
11 });
12
13 CollectionView = BBPlug.BaseView.extend({
14 template: "<h1><% _.each(items, function(item){ %><%= message %> <% } %>!</h1>",
15
16 serializeData: function(){
17 return {
18 items: this.collection.toJSON();
19 };
20 }
21 });
For a more complete list of options and possibilities for namespacing and providing protection for your code, and why, see Stoyan Stefanov’s JavaScript Patterns from O’Reilly press.
Now the three views that I am working with are all extending from the correct BBPlug.BaseView. Any additional objects, functions, data, configuration or other bits that my plugin needs will also hang from the BBPlug object. This way, my plugin will not interfere with other plugins and libraries. I only need to be careful to remember to use the BBPlug namespace and always declare my variables with the var keyword. But I honestly don’t worry about this too much. Tools such as JSHint will analyze my code and ensure that I am correctly namespacing and declaring everything.
This simple object literal as namespace will be sufficient for now. Later, the Revealing Module pattern will be used to help create privacy and encapsulation. But that can wait.
Lessons Learned
Java, C# and other languages provide an explicit construct for namespacing, but JavaScript does not. This does not mean that the concept is not important, though. Namespacing can and should be done in JavaScript. It just has to be done within the constructs that JavaScript provides - as more of an idea than an explicit syntax.
Don’t Pollute The Global Namespace
Namespacing our abstractions is very important. If we don’t provide a single, global object for all of our code to hang off of, we run the risk of either breaking someone else’s code or library, or having another developer or library break our code. There are several options for implementing a namespace in JavaScript, and none of them are specific to Backbone. But Backbone provides a good example of using a namespace, and we created our own with a simple object literal.