2

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}

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

9 Comments

so if you alter pv.foo, you're actually altering BaseView.prototype.foo that's not entirely true (just language matters I'd say). If he alters the object referenced by pv.foo, yeah, the prototype will change. But if he gives him another value, it'll just shadow the prototype. I think that's a point worth to specify.
@Loamhoof: Good point, I guess I think of "alter pv.foo" to mean something like pv.foo.x = y rather than pv.foo = x and I've internalized all the pointer semantics to the point that it can be difficult to explain. Is the updated version clearer?
@mu is too short: Thank you for point out that there is no copying (neither shallow nor deep) when you instantiate, that solve my doubt. However, I think that's not always true. For example, you can modify your foo attribute if is not an object (jsfiddle.net/7Ndf7). I'm not using var because this I'm working with requirejs and in this case I need to create PageView as a global in order to extend it in another file. Also, you can use a super method to chain the initialise (gist.github.com/maxbrunsfeld/1542120)
@mastortosa: (1) But that's not changing the foo object itself, that is replacing the reference completely. (2) I'm sure require.js has a proper way to handle that situation without globals, half the point of require.js is to manage dependencies after all. (3) That super implementation is not stock Backbone, adding yet another library for an aside would just confuse things.
@mastortosa: Also, numbers for things like foo are fine and safe as there are no mutator methods for numbers in JavaScript; strings are similarly safe (unless my memory fails me and there are mutators for strings in JavaScript).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.