Skip to content

Loading…

put __super__ on the prototype #787

Closed
wants to merge 1 commit into from
@tbranyen
Collaborator

Been doing research on extending Backbone and found this interesting bit:

// Set a convenience property in case the parent's prototype is needed later.
child.__super__ = parent.prototype;

At an initial glance it looks as if it could work this this.__super__.someMethod, but in reality you must call it off the constructor which is assigned directly above.

This patch instead places __super__ on the prototype.

/* One level of extension, calling Backbone.Model.prototype.initialize */

// Current
this.constructor.__super__.initialize.call(this);

// New
this.__super__.initialize.call(this);

// Manually
Backbone.Model.prototype.initialize.call(this);

I figure there must be an intentional reason for why its currently set up the way it is. Any elaboration would be great.

@jashkenas
Owner

Yes __super__ is not intended for you to use it directly, as the underscored name implies.

It's the internal property that CoffeeScript uses to make super() work efficiently, and should not be an enumerable property on the prototype. In JavaScript, you should use the manual style:

Backbone.Model.prototype.initialize.call(this);

The existence of __super__ in Backbone is our one line concession so that you can write:

Model = Backbone.Model.extend

  set: (attrs, options) ->
    super

... and have it work.

@jashkenas jashkenas closed this
@tbranyen
Collaborator

One thing I was thinking of to be fair to the JavaScript users is doing:

child.prototype.super = parent.prototype;

So the JavaScript'ers could do:

this.super.initialize.call(this);
@jashkenas
Owner

Unfortunately, Backbone isn't trying to be a JavaScript OOP library ... we're not providing "special" super() semantics in general. This means that in JavaScript, you should be using the standard pattern:

Parent.prototype.method.call(this, arg);

If we did want to be an OOP library -- we would be providing much more sugar than this.

@yuchi

I propose the creation of a wiki page on CoffeeScript which targets the "How to write coffeescript-compatible libraries in Javascript"

@jashkenas
Owner

Yes, that would make a fine wiki page, but let's be clear: Every JavaScript library is compatible with CoffeeScript -- this is simply a convenience so that super() can be used, even when you're not using the class keyword (which you could also use). A special case.

@blocka

Funny, I never fully understood prototypical inheritance until I used Backbone...but I see people are still running away into their comfort zone.

@ehynds

+1. Why not give regular JS users the convenience instead of just CS users? Part of backbone's elegance is extending views, collections, etc. and calling the "super" method is part of the territory.

@yuchi

While @jashkenas said

Yes __super__ is not intended for you to use it directly, as the underscored name implies.

IMHO it's a super legit use of __super__. But that should be used only when you don't know what your super class is. If you know it you should call it directly.

@philfreo

Just my two cents: "no fair" that Backbone adds special code to support super in CoffeeScript but not regular JavaScript when both could be just as easily done. It's cool that Backbone doesn't want to add lots of OOP support, but supporting this one minor thing in JS as well as CS would be much appreciated. Otherwise having an object that extends multiple methods is error-prone and tedious to refactor when the parent class name changes.

@tonyxiao
Model = Backbone.Model.extend

  set: (attrs, options) ->
    super

Doesn't actually work with coffeeScript, it will output the following javascript, which will throw a reference error on set is not defined

var Model;
Model = Backbone.Model.extend({
  set: function(attrs, options) {
    return set.__super__.constructor.call(this, doesntworkactually);
  }
});
@braddunbar
Collaborator

@tonyxiao I believe what @jashkenas meant was something like the following...?

Model = Bakbone.Model.extend()
Model::set = ->
  # ...
  super

which compiles to

var Model;

Model = Backbone.Model.extend();

Model.prototype.set = function() {
  return Model.__super__.set.apply(this, arguments);
};
@yuchi

What about

class Model extends Backbone.Model
  set: ->
    #...
    super
@theefer

+1

I ended up implementing an invokeSuper() method yesterday which can be used from Backbone hierarchies of classes.

SomeModelClass.extend({
  method: function() {
    this.invokeSuper(arguments);
    // do something
  }
})

It's usable as a mixin, see the code here: https://gist.github.com/3054503

@dstibrany

After reading a few articles on this subject, I ended up going with something like this

(function(Backbone, _) {

    // Extend Backbone Classes with a 'super' function to execute a method of an instance's superclass
    _.each(['Collection', 'Model', 'View', 'Router'], function(className) {
        Backbone[className].prototype.super = function(funcName) {
            var parentPrototype = this.constructor.__super__;
            if (parentPrototype && typeof parentPrototype[funcName] === 'function') {
                return this.constructor.__super__[funcName].apply(this, _.rest(arguments));
            }
        };
    });

})(Backbone, _);

And then invoking it like:

var Account = SomeCustomCollection.extend({
      initialize: function() {
          this.super('initialize');
      }
});

Sucks that you have to pass in the method name as an argument, but I like it otherwise.

@sgreenfield

For those of use wanting this bit of OOP sugar, this looks promising: https://github.com/lukasolson/Backbone-Super

@moos

@dstibrany: good concise code, but YUI compressor fails because of 'super'. Using '_super' works.

Backbone[className].prototype._super = function(funcName) { ...

@yuchi

No guys, it's not a good idea to use such a "super" method.

Let me expose it:

var Root = Backbone.Model.extend({
  log: function (msg) {
   console.log(msg);
  }
});

var Screamer = Root.extend({
  log: function (msg) {
    this.constructor.__super__.log.call(this, msg + '!');
  }
});

var SuperScreamer = Screamer.extend({
  log: function (msg) {
    this.constructor.__super__.log.call(this, 'OH MY GOD, '+msg);
  }
});

var aSuperScreamer = new SuperScreamer();

aSuperScreamer.log('Hallo world'); // ERROR: Maximum call stack exceeded.

Why? Because Screamer.prototype.log calls this.constructor.__super__ which is Screamer.prototype ;)

@mehcode

Just for clarification, if you want to use __super__, use it as follows:

SuperScreamer.__super__.log.call(this, 'OH MY GOD, '+msg);

See http://jsfiddle.net/zEhYZ/

@yuchi

Sorry, I forgot to put the good pattern alongside!

@trusktr

ES6 Classes are almost out, bringing the built-in ability to call super() inside a class method. :+1:

@rochoa rochoa referenced this pull request in CartoDB/cartodb.js
Merged

🔥 backbone elder #957

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 14, 2011
  1. @tbranyen

    put __super__ on the prototype

    tbranyen committed
Showing with 1 addition and 1 deletion.
  1. +1 −1 backbone.js
View
2 backbone.js
@@ -1133,7 +1133,7 @@
child.prototype.constructor = child;
// Set a convenience property in case the parent's prototype is needed later.
- child.__super__ = parent.prototype;
+ child.prototype.__super__ = parent.prototype;
return child;
};
Something went wrong with that request. Please try again.