Super in Backbone
Solution 1
You'll want to use:
Backbone.Model.prototype.clone.call(this);
This will call the original clone()
method from Backbone.Model
with the context of this
(The current model).
From Backbone docs:
Brief aside on super: JavaScript does not provide a simple way to call super — the function of the same name defined higher on the prototype chain. If you override a core function like set, or save, and you want to invoke the parent object's implementation, you'll have to explicitly call it.
var Note = Backbone.Model.extend({
set: function(attributes, options) {
Backbone.Model.prototype.set.apply(this, arguments);
...
}
});
Solution 2
You can also use the __super__
property which is a reference to the parent class prototype:
var MyModel = Backbone.Model.extend({
clone: function(){
MyModel.__super__.clone.call(this);
}
});
Solution 3
Josh Nielsen found an elegant solution for this, which hides a lot of the ugliness.
Just add this snippet to your app to extend Backbone's model:
Backbone.Model.prototype._super = function(funcName){
return this.constructor.prototype[funcName].apply(this, _.rest(arguments));
}
Then use it like this:
Model = Backbone.model.extend({
set: function(arg){
// your code here
// call the super class function
this._super('set', arg);
}
});
Solution 4
Working from the answers given by geek_dave and charlysisto, I wrote this to add this._super(funcName, ...)
support in classes that have multiple levels of inheritance. It's worked well in my code.
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === arguments.callee.caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
return scan.constructor.__super__[funcName].apply(this, _.rest(arguments));
};
A year later I've fixed some bugs and made things faster. Below is the code that I use now.
var superCache = {};
// Hack "super" functionality into backbone.
Backbone.View.prototype._superFn = Backbone.Model.prototype._superFn = function(funcName, _caller) {
var caller = _caller == null ? arguments.callee.caller : _caller;
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
var className = scan.constructor.className;
if (className != null) {
var result = superCache[className + ":" + funcName];
if (result != null) {
for (var i = 0; i < result.length; i++) {
if (result[i].caller === caller) {
return result[i].fn;
}
}
}
}
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
var result = scan.constructor.__super__[funcName];
if (className != null) {
var entry = superCache[className + ":" + funcName];
if (entry == null) {
entry = [];
superCache[className + ":" + funcName] = entry;
}
entry.push({
caller: caller,
fn: result
});
}
return result;
};
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
var args = new Array(arguments.length - 1);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i + 1];
}
return this._superFn(funcName, arguments.callee.caller).apply(this, args);
};
Then given this code:
var A = Backbone.Model.extend({
// className: "A",
go1: function() { console.log("A1"); },
go2: function() { console.log("A2"); },
});
var B = A.extend({
// className: "B",
go2: function() { this._super("go2"); console.log("B2"); },
});
var C = B.extend({
// className: "C",
go1: function() { this._super("go1"); console.log("C1"); },
go2: function() { this._super("go2"); console.log("C2"); }
});
var c = new C();
c.go1();
c.go2();
The output in the console is this:
A1
C1
A2
B2
C2
What's interesting is that class C's call to this._super("go1")
scans the class hierarchy until it gets a hit in class A. Other solutions do not do this.
P.S. Uncomment the className
entries of the class definitions to enable caching of the _super
lookup. (The assumption is that these class names will be unique in the application.)
Solution 5
If you want just to call this._super(); without passing the function name as an argument
Backbone.Controller.prototype._super = function(){
var fn = Backbone.Controller.prototype._super.caller, funcName;
$.each(this, function (propName, prop) {
if (prop == fn) {
funcName = propName;
}
});
return this.constructor.__super__[funcName].apply(this, _.rest(arguments));
}
Better use this plugin: https://github.com/lukasolson/Backbone-Super
Andreas Köberle
Updated on July 16, 2022Comments
-
Andreas Köberle almost 2 years
When I override the
clone()
method of aBackbone.Model
, is there a way to call this overriden method from my implantation? Something like this:var MyModel = Backbone.Model.extend({ clone: function(){ super.clone();//calling the original clone method } })
-
Mauvis Ledford over 11 yearsJust a little background on this answer:
__super__
is a reference to the parent's prototype that Backbone framework makes every time a Backbone model, collection, router, or view is extended. While it's not a standard property it does work cross-browser since its framework generated. However, even the Backbone official docs don't mention this and say to useBackbone.Model.prototype.set.call(this, attributes, options);
method. Both seem to work fine, though. -
Mikael Lepistö almost 11 yearsBackbone docs seems to suggest e.g. Backbone.Model.prototype.set.apply(this, arguments); what is the difference between using prototype.func_name.apply(...) and prototype.func_name.call(...) ?
-
Bobby almost 11 years@MikaelLepistö see the question stackoverflow.com/questions/1986896/…
-
MikeSchinkel almost 11 years@MauvisLedford Is your example code correct or should the
.set.
be.clone.
for the OP's use case? -
Mauvis Ledford almost 11 yearsMine was just a non-related example. It would be
Backbone.Model.prototype.clone.call(this, attributes, options);
in his case. -
Jason M almost 10 yearsYou could also use: this.constructor.__super__
-
monastic-panic over 9 yearsthis only works when there is only one level of implementation A.foo -> B.foo, in the case of A.foo -> B.foo -> C.foo you will get a stack overflow due to B's this refering to itself
-
Tobias Cudnik about 9 yearsThis wont work with the full prototype chain. If the direct super class' prototype doesn't contain the method, there'll be an exception.
-
mab almost 9 yearsMy answer below fixes the problem of having more than one level of inheritance,
-
Emile Bergeron about 8 yearsThat's not an inheritance example and not the same "parent" that is discussed in this thread. Also, the
var self = this
is unnecessary in this case since it's not inside a callback function losing the context. -
Blaine Kasten about 8 yearsTruth. I wrote this code when I was young and dumb. Thanks for the update here. Feel free to edit the post and update it. I don't write Backbone anymore, so I don't feel like I can confidently update it.
-
Emile Bergeron over 7 yearsIt should be
Backbone.Model.prototype.clone
andthis.origclone()
. It's equivalent toBackbone.Model.prototype.clone.call(this)
. -
Emile Bergeron over 7 yearsEditing it won't help since it's not answering the question at hand. I think the only option would be to remove the answer altogether.
-
Emile Bergeron over 7 years@JasonM nope,
this.constructor
is not guaranteed to beMyModel
and it would make a stack overflow ifMyModel
was used as a parent class. -
I_ATE_YOUR_WORK_FILES over 5 yearsI wish I could upvote this answer more than once. First sensible description of super I've found on the internet. This concept always confused me, it's quite otherworldly from standard JavaScript...