Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
A 'super' method for backbone.js (plain javascript)
// This method gives you an easier way of calling super
// when you're using Backbone in plain javascript.
// It lets you avoid writing the constructor's name multiple
// times. You still have to specify the name of the method.
//
// So instead of having to write:
//
// User = Backbone.Model.extend({
// save: function(attrs) {
// this.beforeSave(attrs);
// return User.__super__.save.apply(this, arguments);
// }
// });
//
// You get to write:
//
// User = Backbone.Model.extend({
// save: function(attrs) {
// this.beforeSave(attrs);
// return this._super("save", arguments);
// }
// });
//
;(function(Backbone) {
// The super method takes two parameters: a method name
// and an array of arguments to pass to the overridden method.
// This is to optimize for the common case of passing 'arguments'.
function _super(methodName, args) {
// Keep track of how far up the prototype chain we have traversed,
// in order to handle nested calls to _super.
this._superCallObjects || (this._superCallObjects = {});
var currentObject = this._superCallObjects[methodName] || this,
parentObject = findSuper(methodName, currentObject);
this._superCallObjects[methodName] = parentObject;
var result = parentObject[methodName].apply(this, args || []);
delete this._superCallObjects[methodName];
return result;
}
// Find the next object up the prototype chain that has a
// different implementation of the method.
function findSuper(methodName, childObject) {
var object = childObject;
while (object[methodName] === childObject[methodName]) {
object = object.constructor.__super__;
}
return object;
}
_.each(["Model", "Collection", "View", "Router"], function(klass) {
Backbone[klass].prototype._super = _super;
});
})(Backbone);
window.context = window.describe;
describe("_super", function() {
var Friend, Animal, Mammal, Pet, Dog, CockerSpaniel;
beforeEach(function() {
Friend = Backbone.Model.extend({
greet: function(personName, timeOfDay) {
return "Good " + timeOfDay + ", " + personName + ". My name is " + this.get("name") + ".";
}
});
// super needs to work even when there are classes in the
// inheritance hierarchy that do not override the method.
Animal = Friend.extend({ eats: "food" });
Mammal = Animal.extend({
greet: function(personName, timeOfDay) {
return this._super("greet", arguments) + " I'm a mammal.";
}
});
Pet = Mammal.extend({ livesInCaptivity: true });
Dog = Pet.extend({
greet: function(person, timeOfDay) {
return this._super("greet", arguments) + " Ruff ruff!";
}
});
CockerSpaniel = Dog.extend({ cute: true });
spyOn(Friend.prototype, 'greet').andCallThrough();
spyOn(Mammal.prototype, 'greet').andCallThrough();
spyOn(Dog.prototype, 'greet').andCallThrough();
});
context("when used only once in the inheritance hierarchy", function() {
context("in the class's own implementation of the method", function() {
beforeEach(function() {
this.friend = new Mammal({ name: "Benjie" });
});
itCallsTheOverriddenMethodCorrectly();
});
context("in a superclass's implementation of the method", function() {
beforeEach(function() {
this.friend = new Pet({ name: "Benjie" });
});
itCallsTheOverriddenMethodCorrectly();
it("does not call the object's own method more than once", function() {
this.friend.greet("Barbara", "morning");
expect(Mammal.prototype.greet.callCount).toBe(1);
});
});
});
context("when used twice in the inheritance hierarchy", function() {
context("with the first case happening in the class's own implementation", function() {
beforeEach(function() {
this.friend = new Dog({ name: "Benjie" });
});
itCallsTheOverriddenMethodCorrectly();
it("calls both of the ancestor classes' methods", function() {
var greeting = this.friend.greet("Barbara", "morning");
expect(greeting).toContain("I'm a mammal.");
});
});
context("with the first case happening in a superclass's implementation", function() {
beforeEach(function() {
this.friend = new CockerSpaniel({ name: "Benjie" });
});
itCallsTheOverriddenMethodCorrectly();
it("does not call the object's own method more than once", function() {
this.friend.greet("Barbara", "morning");
expect(Dog.prototype.greet.callCount).toBe(1);
});
});
});
context("when the overridden method calls super by referencing its constructor explicitly", function() {
beforeEach(function() {
Mammal.prototype.greet = function(personName, timeOfDay) {
return Mammal.__super__.greet.apply(this, arguments) + " I'm a mammal.";
}
spyOn(Mammal.prototype, 'greet').andCallThrough();
this.friend = new CockerSpaniel({ name: "Benjie" });
});
itCallsTheOverriddenMethodCorrectly();
});
function itCallsTheOverriddenMethodCorrectly() {
it("passes the given arguments to the overridden method", function() {
var greeting = this.friend.greet("Barbara", "morning");
expect(greeting).toContain("Good morning, Barbara.");
});
it("calls the overridden method on the recieving object", function() {
var greeting = this.friend.greet("Barbara", "morning");
expect(greeting).toContain("My name is Benjie.");
});
it("calls the overridden method only once", function() {
this.friend.greet("Barbara", "morning");
expect(Friend.prototype.greet.callCount).toBe(1);
expect(Mammal.prototype.greet.callCount).toBe(1);
});
it("can be called multiple times with the same results", function() {
var greeting = this.friend.greet("Barbara", "morning");
expect(this.friend.greet("Barbara", "morning")).toBe(greeting);
expect(this.friend.greet("Barbara", "morning")).toBe(greeting);
});
}
});
@chikamichi

Pretty cool. Thanks for the inspiration.

@kalebdf

I like this simple approach! Max @maxbrunsfeld, what is the license on this Gist? MIT?

@mastortosa

Thanks!
Also, you can use _super to return attributes, not only methods, just change:

var result = parentObject[methodName].apply(this, args || []);

to:

var result = parentObject[methodName]; // Attribute. Maybe change the variable name.
if (_.isFunction(result)) result = result.apply(this, _.rest(arguments));

@longlho

hmm doesn't seem to work when overriding constructor, for ex:

this._super('constructor', arguments)

@tyler08

i agree it doesn't work with this._super('initialize', arguments) , i use the latest backbone js release. It seems that the argument are not passed ????

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.