I am working on a big Backbone.js application. The code is modular structured using require.js
.
Now I see a lot of backbone code and tutorials doing this:
window.app = ( window.app || {} );
after they will assign model definitions and Collection instances to that global object, like so:
Task = Backbone.Model.extend({ /*...*/ });
Tasks = Backbone.Collection.extend({ /*...*/ });
window.app.Task = Task;
window.app.Tasks = new Tasks();
// do this with all your models and collections
I like this approach for its simplicity and for not having to deal with where and when to instantiate a collection. But it somehow seems wrong to first separate the code into tiny bits using require.js
and after assign all together to a global variable (apart from that global variables are generally bad codestyle in javascript).
So what is your take on this, what are the pros and cons of this approach and how do you deal with your objects in Backbone?
First of all its not that big a problem to pollute your global name space as you can wrap all your model in your own namespace:
app = (app || {} );
app.Task = Backbone.Model.extend({ /*...*/ });
app.Tasks = Backbone.Collection.extend({ Model:app.Zask });
When it comes to requirejs it depends on the size of your project. I think for smaller projects you can go without. The benefit of using requirejs isn't the preventing of namespace pollution, its the better maintainability because you always see the dependencies of a module. You can write better unit tests, because you can test a module and mock out all its dependencies. So we rebuild a large Backbone app from a structure like the above example into something like this:
models/Task.js
define(function () {
return Backbone.Model.extend({ /*...*/ });
});
collections/Tasks.js
define([models/Task], function (Task) {
return Backbone.Collection.extend({ Model:app.Task });
});
app.js
define([collections/Tasks], function (Tasks) {
var tasks = new Tasks();
tasks.fetch();
});
I disagree that globals are bad in javascript; you need globals in most languages. After you import something, you need to be able to refer to it. Java-like languages just throw all class-names onto some giant global heap; but you never realise it's so because in those languages classes (class-names, class-references) are not variables.
In javascript classes (or what pretends to mimic classes) are just objects, which you assign to variables. So it's up to you to make sure you can refer to them in some other part of your code.
Now, actually in your example, you assign a model definition, and a collection instance (presumably a singleton) to the global scope. For the model definition, I would say it's the right and only way to do it (perhaps add some namespacing, such as window.app.model.Task = Task
). In the second case, a purist's view might be that it's not so nice. I would prefer to do something like window.app.collection.Tasks = Tasks
, and then define a static method Tasks.getinstance()
, which returns the singleton. Alternatively, less nice (but less coding) would be to put your singletons in the global scope as well window.app.singletons.Tasks = Tasks()
.
Finally, make sure (as I'm sure you have) that all this is properly scoped. This code (run in the browser) will also make Task and Tasks global objects. Wrap this whole thing in a function, and use var
(function (app) {
var Task = Backbone.Model.extend({ /*....*/ }),
Tasks = Backbone.Collection.extend({ /*....*/ }, {
getInstance: function () {
if (!this.instance) {
this.instance = new Tasks();
}
return this.instance;
}
});
if (!app.models) {
app.models = {};
}
if (!app.collections) {
app.collections = {};
}
app.models.Task = Task;
app.collections.Tasks = Tasks;
}(window.app));
I personally would not pollute the global namespace which is along the lines of an AMD-solution. Here is a post on sharing resources across AMD modules which is basically what you want to do but with a model and collection.
Also, at the bottom of this issue for Backbone Aura framework there is an implementation of a data module that namespaces collections and models for retrieval. I just came across this problem in my app the other day and this is what I plan on implementing.