mmas
mmas

Reputation: 78

Problems with Backbone.js inheritance

I believed I had a good background in backbone.js but I have a problem I don't understand.

Suppose we have these two views:

BaseView = Backbone.View.extend({

    foo: {},

    initialize: function(options) {
        this.render();
    },

    render: function() {
        // ...
    }
});

PageView = BaseView.extend({

    render: function() {
        this.foo.bar = 23;
    }
});

If we check the attribute 'foo' in both views, obviously, it will be an empty object:

> BaseView.prototype.foo
  Object {}
> PageView.prototype.foo
  Object {}

But if we create a PageView instance, the 'foo' attribute from BaseView will be changed. How is that possible?

> page = new PageView()
> page.foo
  Object {foo: 23}
> BaseView.prototype.foo
  Object {foo: 23}

[Edit]

Actually, it's a general OOP question. In python:

class A(object):
    foo = {}

class B(A):

    def __init__(self):
        self.foo['bar'] = 23

>>> print A.foo
{}
>>> b = B()
>>> print A.foo
{'bar': 23}

Upvotes: 2

Views: 188

Answers (1)

mu is too short
mu is too short

Reputation: 434615

When you do this:

BaseView = Backbone.View.extend({
    foo: {},
    //...
});

That foo: { } gets attached to the BaseView prototype. That means that that exact object is shared by all instances of BaseView and by all instances of any BaseView "subclasses". So given this:

var BaseView = Backbone.View.extend({
    foo: {},
    //...
});
var PageView = BaseView.extend({
    //...
});
var bv = new BaseView;
var pv = new PageView;

the data really looks like this:

bv.foo -->--+-->-- BaseView.prototype.foo -->-- { }
            |
pv.foo -->--/

so if you alter pv.foo with something like pv.foo.x = y, you're actually altering BaseView.prototype.foo and that's the same object as bv.foo; of course if you pv.foo = something_else then you've changed the reference and bv.foo and pv.foo will no longer refer to the same underlying object. You have only one underlying object with multiple references to it.

There is no copying (neither shallow nor deep) when you instantiate or extend your BaseView or PageView, you're just sharing a single reference to the underlying object.

If you want your foo to be instance-specific (as you almost always do), then set it up in your constructor:

var BaseView = Backbone.View.extend({
    initialize: function() {
        this.foo = { };
        //...
    },
    //...
});

Of course, the initialize methods don't chain by themselves so you'll have to do that yourself if your PageView needs one:

var PageView = BaseView.extend({
    initialize: function() {
        BaseView.prototype.initialize.apply(this, arguments);
        // Whatever else you need done...
    },
    //...
});

Demo: http://jsfiddle.net/ambiguous/4duQ5/


Also, you really should be saying var BaseView rather than just BaseView, no var means that you're creating a global and you probably don't want to do that.

Upvotes: 2

Related Questions