Backbone.js custom constructor?

2019-03-14 19:53发布

问题:

I'm looking for some examples for creating a custom constructor on my models. I want the structure the model/data differently then just setting it as attributes.

Can somebody show me some basic example of how to do this?

Thanks!

回答1:

If you really want to override the constructor, pass a constructor property to Backbone.Model.extend(), e.g.:

var Klass = Backbone.Model.extend( {

  constructor : function ( attributes, options ) {

    // ...

  }

} );

If you want to call the built-in constructor from your custom constructor, you can do something like:

var Klass = Backbone.Model.extend( {

  constructor : function ( attributes, options ) {

    Backbone.Model.apply( this, arguments );

  }

} );

Or if you don't want to have to repeat the name of the variable containing the parent class all over the sub class, or you don't want to worry about the value of that variable changing, you can do something like the following:

var Klass;

var parent_klass = Backbone.Model.prototype;

( function ( parent_klass ) {

  Klass = parent_klass.constructor.extend( {

    constructor : function ( attributes, options ) {

      parent_klass.constructor.apply( this, arguments );

    }

  } );

} )( parent_klass );

Or if you prefer the way @Claude suggests, but repeating the sub class variable name within the sub class instead of the parent class var name:

var Klass = Backbone.Model.extend(

  {

    constructor : function ( attributes, options ) {

      Klass.parent_klass.constructor.apply( this, arguments );

    }

  },

  {

    parent_klass : Backbone.Model.prototype

  }

);

If you want more advice than that, you'll have to be more specific about what you want to accomplish.

Anything that you just want to do after the built-in constructor functionality, you should probably do in initialize().



回答2:

As I mention in the comment, take care when using this.constructor.__super__ (or this.__super__), lest you end up in an endless loop (Maximum call stack size exceeded in Chrome).

Try the following in the console (at own risk, it will lead to afore mentioned endless loop)

var A = Backbone.Model.extend({constructor: function () {
  console.log("log: A");
  this.constructor.__super__.constructor.apply(this, arguments);
}});

var B = A.extend({constructor: function () {
  console.log("log: B");
  this.constructor.__super__.constructor.apply(this, arguments);
}});

new A();
//  log: A
//  > Backbone.model.extend.constructor
new B();
//  log: B
//  (8192) log: A
//  > RangeError: Maximum call stack size exceeded

Reason is that when creating B, this in the constructor for A points to an instance of B, so this.constructor.__super__.constructor keeps pointing towards the constructor of A, which is being called time and time again.

If you want "intended behaviour", use one of the following syntaxes:

var A = Backbone.Model.extend({constructor: function () {
  console.log("log: A");
  A.__super__.constructor.apply(this, arguments);
}});

var B = A.extend({constructor: function () {
  console.log("log: B");
  B.__super__.constructor.apply(this, arguments);
}});

new A();
//  log: A
//  > Backbone.model.extend.constructor
new B();
//  log: B
//  log: A
//  > A.extend.constructor

or directly without __super__:

var A = Backbone.Model.extend({constructor: function () {
  console.log("log: A");
  Backbone.Model.apply(this, arguments);
}});

var B = A.extend({constructor: function () {
  console.log("log: B");
  A.apply(this, arguments);
}});

new A();
//  log: A
//  > Backbone.model.extend.constructor
new B();
//  log: B
//  log: A
//  > A.extend.constructor


回答3:

This is how I override the default Backbone.Model constructor:

Backbone.Model = (function(Model) {
  // Define the new constructor
  Backbone.Model = function(attributes, options) {
    // Your constructor logic here
    // ...
    // Call the default constructor if you wish
    Model.apply(this, arguments);
    // Add some callbacks
    this.on('event', this.myCallback, this);
  };
  // Clone static properties
  _.extend(Backbone.Model, Model);      
  // Clone prototype
  Backbone.Model.prototype = (function(Prototype) {
    Prototype.prototype = Model.prototype;
    return new Prototype;
  })(function() {});
  // Update constructor in prototype
  Backbone.Model.prototype.constructor = Backbone.Model;
  return Backbone.Model;
})(Backbone.Model);

After that you have to make sure the Backbone.Collection prototype uses the updated Backbone.Model constructor:

_.extend(Backbone.Collection.prototype, {
  model: Backbone.Model
});

The 'advantage' of this approach in my opinion is that you can keep using Backbone.Model as your model constructor, which makes the change more transparent.



回答4:

If you want to write your model by yourself, like this:

var YourModel = function () {
    // your constructor here
};

_.extend(YourModel.prototype, Backbone.Model.prototype, {
    // your model method here
});

Be careful, I think you need to consult Backbone.Model constructor source code. But I think this is not a good idea. Override initialize method is the right way:

var YourModel = Backbone.Model.extend({
    initialize: function (attrs, options) {
        Backbone.Model.prototype.initialize.apply(this, arguments); // call super constructor

        // your constructor code here
    }
});


回答5:

It sounds like you're looking for the initialize method. It's called when you create a new model, and you can use if for whatever you need it for:

var myModel = Backbone.Model.extend({
  initialize: function() {
    // do something besides setting attributes or model
    alert('myModel is ready for action!');
  }
});

new myModel();

If you're looking to do something more involved, you might override the constructor method in Backbone core. That's a much trickier business, though. Much better to work with exposed methods if you can!



回答6:

Something to be aware of when overriding the Backbone.Model constructor - if you don't call Backbone.Model.apply then the Model.cid may not be set.

That's bad - If cid is not set across multiple models, collection may consider it to be a duplicate of the first model in your collection - and will not allow it to be added.