Reputation: 1404
I've been playing with backbone view a bit lately and I just encountered a VERY strange behavior when creating my own view subclasses :
It seems to me that calling extend() on subclasses of Backbone.View tries to merge object attributes in the superclass definition (sorry for the barbaric phrasing) .
Please consider the following code :
(function() {
var SomeCustomView = Backbone.View.extend({
children : {},
//internal use only
_children : {},
counter : 0,
initialize : function(options){
},
render : function(){
var t = this;
_.each(this.children, function(child, childId){
if(_.isFunction(child))child=child.call(this);
this._children[childId] = child;
this.counter++;
},t );
return this;
},
});
//register on window object
this.SomeCustomView = SomeCustomView;
}).call(this);
then creating subclasses of SomeCustomView :
(function() {
console.error("START STRANGE BUG TEST");
var BetterCustomView = SomeCustomView.extend({
children : {
firstChild : { name : 'hi'}
}
});
var instance1 = new BetterCustomView({ id : 'one' });
console.error(" _____________________________ ");
console.error("instance1.counter before render" + instance1.counter);
instance1.render();
console.error("instance1.counter after render" + instance1.counter);
console.error("instance1 _children");
console.error(instance1._children);
console.error("instance1._children.firstChild");
console.error(instance1._children.firstChild);
console.error("instance1._children.secondChild");
console.error(instance1._children.secondChild);
console.error(" _____________________________ ");
var EvenBetterCustomView = SomeCustomView.extend({
children : {
firstChild : { name : 'wuazza', foo : 'bar' },
secondChild : { name : 'NotSupposedToBeInUltimate'}
}
});
var instance2 = new EvenBetterCustomView({ id : 'two' });
console.error(" _____________________________ ");
console.error("instance2.counter before render" + instance2.counter);
instance2.render();
console.error("instance2.counter after render" + instance2.counter);
console.error("instance2 _children");
console.error(instance2._children);
console.error("instance2._children.firstChild");
console.error(instance2._children.firstChild);
console.error("instance2._children.secondChild");
console.error(instance2._children.secondChild);
console.error(" _____________________________ ");
var TheUltimateCustomView = SomeCustomView.extend({
children : {
firstChild : { name : 'whizzz' }
}
});
var instance3 = new TheUltimateCustomView({ id : 'three' });
console.error(" _____________________________ ");
console.error("instance3.counter before render" + instance3.counter);
instance3.render();
console.error("instance3.counter after render" + instance3.counter);
console.error("instance3 _children");
console.error(instance3._children);
console.error("instance3._children.firstChild");
console.error(instance3._children.firstChild);
console.error("instance3._children.secondChild");
console.error(instance3._children.secondChild);
console.error(" _____________________________ ");
}).call(this);
now the console outputs the following :
START STRANGE BUG TEST test.html:46
_____________________________ test.html:56
instance1.counter before render0 test.html:58
instance1.counter after render1 test.html:60
instance1 _children test.html:62
Object {firstChild: Object}
test.html:63
instance1._children.firstChild test.html:65
Object {name: "hi"} test.html:66
instance1._children.secondChild test.html:67
undefined test.html:68
_____________________________ test.html:70
_____________________________ test.html:83
instance2.counter before render0 test.html:85
instance2.counter after render2 test.html:87
instance2 _children test.html:89
Object {firstChild: Object, secondChild: Object}
test.html:90
instance2._children.firstChild test.html:92
Object {name: "wuazza", foo: "bar"} test.html:93
instance2._children.secondChild test.html:94
Object {name: "NotSupposedToBeInUltimate"} test.html:95
_____________________________ test.html:97
_____________________________ test.html:110
instance3.counter before render0 test.html:112
instance3.counter after render1 test.html:114
instance3 _children test.html:116
Object {firstChild: Object, secondChild: Object}
test.html:117
instance3._children.firstChild test.html:119
Object {name: "whizzz"} test.html:120
instance3._children.secondChild test.html:121
Object {name: "NotSupposedToBeInUltimate"} test.html:122
_____________________________
Notice that each time in the actual tests I extend SomeCustomView. Notice how in the third subclass TheUltimateCustomView you can find the 'secondChild' that was declared inside EvenBetterCustomView and placed inside the _children object during the call to render() ;
To me this is a very odd behavior to say the least.
TheUltimateCustomView does not extend EvenBetterCustomView in which secondChild was declared. Moreover, we are testing the _children object which is populated during a call to render() on an SUBCLASS's INSTANCE of our CustomViewClass. How does it end up in other subclasses CustomViewClass ... ?
Can someone explain this to me ? Is this a bug in how BackBone.View performs its .extend method ?
Is it me doing something terribly wrong ?
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="underscore.js"></script>
<script type="text/javascript" src="backbone.js"></script>
<script type="text/javascript">
(function() {
var SomeCustomView = Backbone.View.extend({
children : {},
//internal use only
_children : {},
counter : 0,
initialize : function(options){
},
render : function(){
var t = this;
_.each(this.children, function(child, childId){
if(_.isFunction(child))child=child.call(this);
this._children[childId] = child;
this.counter++;
},t );
return this;
},
});
//register on window object
this.SomeCustomView = SomeCustomView;
}).call(this);
(function() {
console.error("START STRANGE BUG TEST");
var BetterCustomView = SomeCustomView.extend({
children : {
firstChild : { name : 'hi'}
}
});
var instance1 = new BetterCustomView({ id : 'one' });
console.error(" _____________________________ ");
console.error("instance1.counter before render" + instance1.counter);
instance1.render();
console.error("instance1.counter after render" + instance1.counter);
console.error("instance1 _children");
console.error(instance1._children);
console.error("instance1._children.firstChild");
console.error(instance1._children.firstChild);
console.error("instance1._children.secondChild");
console.error(instance1._children.secondChild);
console.error(" _____________________________ ");
var EvenBetterCustomView = SomeCustomView.extend({
children : {
firstChild : { name : 'wuazza', foo : 'bar' },
secondChild : { name : 'NotSupposedToBeInUltimate'}
}
});
var instance2 = new EvenBetterCustomView({ id : 'two' });
console.error(" _____________________________ ");
console.error("instance2.counter before render" + instance2.counter);
instance2.render();
console.error("instance2.counter after render" + instance2.counter);
console.error("instance2 _children");
console.error(instance2._children);
console.error("instance2._children.firstChild");
console.error(instance2._children.firstChild);
console.error("instance2._children.secondChild");
console.error(instance2._children.secondChild);
console.error(" _____________________________ ");
var TheUltimateCustomView = SomeCustomView.extend({
children : {
firstChild : { name : 'whizzz' }
}
});
var instance3 = new TheUltimateCustomView({ id : 'three' });
console.error(" _____________________________ ");
console.error("instance3.counter before render" + instance3.counter);
instance3.render();
console.error("instance3.counter after render" + instance3.counter);
console.error("instance3 _children");
console.error(instance3._children);
console.error("instance3._children.firstChild");
console.error(instance3._children.firstChild);
console.error("instance3._children.secondChild");
console.error(instance3._children.secondChild);
console.error(" _____________________________ ");
}).call(this);
</script>
</head>
<body>
coucou
</body>
</html>
Below is the full html page in which you can test this behavior :
Thank you very much for your help.
Upvotes: 0
Views: 74
Reputation: 4129
To be more precise, when you do like this :
(function() {
var SomeCustomView = Backbone.View.extend({
...
//internal use only
_children : {},
...
});
every instance has it's own _children
field, but they all share the same initialized value {}
, so all the views that will extend SomeCustomView
will share the same value unless you change _children
to another value.
Try something like this :
(function() {
var SomeCustomView = Backbone.View.extend({
children : {},
//internal use only
_children : null, // or whatever you want
...
render : function(){
this._children = {}; // here your instance will have it's own value
...
},
});
Upvotes: 1