Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why instanceof keyword does not work with Backbone js models?

I am building a Backbone JS application that involves inheritance and the use of instanceof javascript keyword.

I have the following code :

app.Sport = Backbone.Model.extend
({
    defaults:
    {
        id: 0,
        title: 'Running'
    }
});

Further in the code, I instantiate a new Sport by typing :

var newSport = new app.Sport ();

I may manipulate this newly created instance without issues.

But, because there's a but, the instanceof keyword always return false when asking for the type of my instance :

console.log ('is newSport a Sport instance ? ' + newSport instanceof app.Sport);

always display false. Why ?

Note : I did not mention inheritance in my question, as it does not even work with the simple form of OOP (one instance of the base class, and asking for the type just after).

My initial purpose is to trigger specific action depending on the type of the sport (thus the use of instanceof keyword) ; it could be a cool one, or an extreme one :

app.CoolSport = app.Sport.extend ({ ... });
app.ExtremeSport = app.Sport.extend ({ ... });

EDIT : I isolated the issue. It is not linked to instanceof keyword or the Model I declared. Rather, I populate a Backbone collection, and push to it some different kind of sports. Here is the test code : (Fiddle)

var app = {};


app.Sport = Backbone.Model.extend
({
    defaults:
    {
        id: 0,
        title: 'Running'
    }
});


app.CoolSport = app.Sport.extend
({
    defaults:
    {
        uselessAttr:'I am a Cool Sport !'
    }
});



app.SportList = Backbone.Collection.extend
({
    model: app.Sport

});


var coolSport1 = new app.CoolSport();

console.log ('is coolSport1 a Sport instance ? ' , coolSport1 instanceof app.Sport);

console.log ('is coolSport1 a CoolSport instance ? ' , coolSport1 instanceof app.CoolSport);

console.log ('is coolSport1 a CoolSport instance (wrong operand in console) ? ' + coolSport1 instanceof app.CoolSport);


var sportList = new app.SportList();
sportList.push (coolSport1.toJSON());


sportList.each ( function ( sport )
{
    if ( sport instanceof app.CoolSport )
    {
        console.log ( "sport is an instance of Cool Sport ! Yeah !" , sport instanceof app.CoolSport ) ;
    }                       
    else
    {
        console.log ( "Not a CoolSport instance..");
    }
});

And guess what ? my CoolSport instance... is not a CoolSport instance. I suspect the init of the Backbone SportList collection to be the issue.

Any ideas ?

like image 372
jeromedt Avatar asked Aug 21 '13 09:08

jeromedt


2 Answers

Please, do not this:

console.log ('is newSport a Sport instance ? ' + newSport instanceof app.Sport);

Do it right:

console.log ('is newSport a Sport instance ? ' + (newSport instanceof app.Sport));

or

console.log ('is newSport a Sport instance ? ', newSport instanceof app.Sport);
like image 165
Sergey Avatar answered Oct 09 '22 11:10

Sergey


I found the solution :-)

I commented the code below :

var coolSport1 = new app.CoolSport();

// Of course it is
console.log ('is coolSport1 a Sport instance ? ' , coolSport1 instanceof app.Sport);

console.log ('is coolSport1 a CoolSport instance ? ' , coolSport1 instanceof app.CoolSport);


var sportList = new app.SportList();

// Here is the tricky part : we push the JSON representation of our CoolSport instance
sportList.push (coolSport1.toJSON());


// Backbone does not know anymore the subtype of our instance, as it has been JSON stringified juste before.
sportList.each ( function ( sport )
{
    if ( sport instanceof app.CoolSport )
    {
        console.log ( "sport is an instance of Cool Sport ! Yeah !" , sport instanceof app.CoolSport ) ;
    }                       
    else
    {
        console.log ( "Not a CoolSport instance..");
    }
});

Backbone documentation provides a mechanism for this behaviour :

var Library = Backbone.Collection.extend({

  model: function(attrs, options) {
    if (condition) {
      return new PublicDocument(attrs, options);
    } else {
      return new PrivateDocument(attrs, options);
    }
  }

});

It tells the collection how to process a JSON representation, in order to add a correct subtype instance to the list. So, to add a CoolSport while keeping the code untouched, one just need to add the model function in the SportList collection, and returning a new CoolSport if it contains 'uselessAttr' (see the code in my question).

I took a different way, and directly pass a CoolSport instance through add() method of the collection, thus preserving its subtype.

Thanks everybody for help, and hope my answer will help others :-)

like image 2
jeromedt Avatar answered Oct 09 '22 11:10

jeromedt