user888734
user888734

Reputation: 3897

Backbone and RequireJS conflicts - instances or constructors?

Could someone explain the fundamental difference between:

define(['backbone'], function(Backbone) {
   MyModel = Backbone.Model.extend({
   });
});

define(['backbone', 'models/mymodel'], function(Backbone){
    var app = Backbone.View.extend({
        initialize: function() {
           var model = new MyModel();
        }
    });
});

and:

define(['backbone'], function(Backbone) {
   var MyModel = Backbone.Model.extend({
   });
   return MyModel;
});

define(['backbone', 'models/mymodel'], function(Backbone, MyModel){
    var app = Backbone.View.extend({
        initialize: function() {
           var model = new MyModel();
        }
    });
});

In the former, the first module simply defines MyModel. In the latter, it's created as a variable and returned, and the second module needs to have it put in the parameters when imported.

RequireJS examples I see around seem to vary between the two, but I don't really understand the difference - does one return an instance and the other a constructor?

In my application I didn't even notice that I was actually using both ways in different places, and I think it was causing problems. I was using a lot of

self = this
self.model.doSomething

inside my views and models, and as my app got bigger, I started getting errors because there were conflicts with definitions of self.

Upvotes: 2

Views: 911

Answers (2)

machineghost
machineghost

Reputation: 35760

Short Version: 1st version == wrong.

Medium Version: The first one bypasses Require entirely by using global variables, while the second one actually uses Require.

Long version:

The way Backbone modules work is that you run "define", pass it a function (and usually an array of dependencies also), and whatever gets returned from that function is defined as that module. So if I do:

// Inside foo.js
define([], function() {
   return 1;
});

I've defined the "foo" module to be 1, so if elsewhere I do:

define(['foo'], function(foo) {
    alert(foo); // alerts 1
});

Your first version doesn't return anything, so it's not actually creating a Require module at all.

How does it work then? Well, in that version you do:

MyModel = Backbone.Model.extend({

NOT:

var MyModel = Backbone.Model.extend({

So that's really the same as doing:

window.MyModel = Backbone.Model.extend({

Then when the second part of the code runs, it access window.MyModel, and works ... but it's completely bypassing Require.js in the process.

I think the most important thing to takeaway is: ALWAYS DECLARE (ie. var) YOUR JAVASCRIPT VARIABLES. I don't agree with everything Crockford says, but he's dead right on this one. You will get lots of bugs (with Require and without) if you don't make this a habit.

Beyond that, the next most important thing is probably: ALWAYS RETURN SOMETHING FROM THE FUNCTION YOU PASS TO define. There are certain special cases where you don't want to return anything, but unless you are deliberately trying to solve one of those cases you should always return something to define the module.

Finally, if you're using Require, every variable in your code should either:

  • Come from the define function (ie. it should be an argument variable from the function that you pass to define), or
  • It should be declared (ie. var-ed ) inside that file

If you use JSLint or 'use strict'; (as Valentin Nemcev suggested), or if you use an editor like Eclipse, your tools can help you ensure this (and in fact make it easy to ensure).

Upvotes: 7

Valentin Nemcev
Valentin Nemcev

Reputation: 4960

MyModel = Backbone.Model.extend({});

Here you are not returning a constructor, you are defining a global variable and accessing it later in different module.

Actually it is wrong, it works by accident. You should return your modules from define and access them via parameters in other modules.

Like this:

return Backbone.Model.extend({});

You should use strict mode to avoid problems with global variables in JS.

Also, constructor in JS is just a function that is meant to be run with new. Backbone extend always returns a constructor function, and you create a model instance by calling the constructor with new, like you are doing in both examples.

Upvotes: 2

Related Questions