Reputation: 5174
I have a backbone class:
class BaseClass extends Backbone.Model
defaults:
first_name: "John"
class ExtendClass extends BaseClass
defaults:
last_name: "Doe"
However, when I make a new ExtendClass
, it does not have first_name
.
Am I misunderstanding how Backbone extends?
Upvotes: 1
Views: 105
Reputation: 2662
The extend()
helper in Backbone allows you to extend prototypes. In plain JavaScript without Backbone's extend method we could setup prototype chains like in the following example:
function BaseClass(opts) {
// Here `this.defaults` will point to `BaseClass.prototype.defaults`
// when we create an instance of BaseClass.
//
// When we create an instance of ChildClass `this.defaults` will
// point to `ChildClass.prototype.defaults` if it exists, otherwise
// it will point to `BaseClass.prototype.defaults`.
this.attrs = _.extend({}, this.defaults, opts);
// Call `BaseClass.prototype.initialize` or `ChildClass.prototype.initialize`,
// depending on what instance we're creating.
this.initialize();
}
BaseClass.prototype.defaults = {
first_name: 'anonymous',
friend_count: 500
};
BaseClass.prototype.initialize = function() {
console.log('Hello ' + this.attrs.first_name);
};
function ChildClass(opts) {
// Let the base class set the `attrs` property
// and call the initialize() method.
BaseClass.call(this, opts);
}
// Set the prototype chain
ChildClass.prototype = Object.create(BaseClass.prototype);
// Create own `defaults` object, therefore overriding
// the one higher in the prototype chain.
ChildClass.prototype.defaults = {
last_name: 'backbone',
parent_name: 'Unknown'
};
ChildClass.prototype.initialize = function() {
// Do something...
};
From the example above you can see that the ChildClass sets its own defaults
property on its prototype, therefore when one of ChildClass instances queries for defaults
, JavaScript will find the one in the ChildClass prototype object and not look any further.
This is exactly what happens in the background with Backbone. Backbone will look for a property named defaults
and will merge it with the instance's attributes, and because ChildClass has its own defaults
property, Backbone will not look any further, and it will not merge the BaseClass defaults automatically for you.
However, in Backbone you can set the defaults
property to a function, and you can then manually access the BaseClass's defaults in order to merge them with the ChildClass's defaults. This manual wiring is intentional as Backbone doesn't want to assume anything about your object hierarchies (in this case your Models).
In JavaScript that would look like this:
var Note = Backbone.Model.extend({
defaults: {
title: 'Untitled',
message: 'Lorem ipsum dolor...'
},
initialize: function() { /* ... */ },
});
var PrivateNote = Note.extend({
defaults: function() {
var defaults = _.extend({}, { date: new Date() }, Note.prototype.defaults);
// You can also do this if you don't want to specify `Note` by its name:
// var defaults = _.extend({}, { date: new Date() }, this.constructor.__super__.defaults);
// `__super__` is specific to Backbone and in this case
// it gives you access to `Note.prototype`.
return defaults;
},
initialize: function() { /* ... */ },
});
Now we get what we want:
var private = new PrivateNote();
private.toJSON();
//=> {date: Mon Feb 09 2015 00:57:14 GMT-0500 (EST), title: "Untitled", message: "Lorem ipsum dolor..."}
Note instances are untouched as we would expect:
var note = new Note()
note.toJSON();
//=> {title: "Untitled", message: "Lorem ipsum dolor..."}
Here is a js2.coffee example: Merge defaults with Parent defaults
Upvotes: 2
Reputation: 434665
I think you're misunderstanding how CoffeeScript extends. When you say:
class A
p: ...
class B extends A
p: ...
The B::p
value completely hides A::p
, CoffeeScript won't try to merge them or chain them or do anything special.
The defaults
in a Backbone model can be an object or a function so you can use functions and CoffeeScript's super
to merge them yourself:
class BaseClass extends Backbone.Model
defaults: ->
first_name: "John"
class ExtendClass extends BaseClass
defaults: ->
_(super).extend(last_name: "Doe")
That will give you both first_name
and last_name
in the ExtendClass
defaults.
Demo: http://jsfiddle.net/ambiguous/wz6usg36/
Upvotes: 3