put __super__ on the prototype #787

Closed
wants to merge 1 commit into
from

Conversation

Projects
None yet
@tbranyen
Collaborator

tbranyen commented Dec 14, 2011

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

This comment has been minimized.

Show comment
Hide comment
@jashkenas

jashkenas Dec 14, 2011

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.

Owner

jashkenas commented Dec 14, 2011

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 Dec 14, 2011

@tbranyen

This comment has been minimized.

Show comment
Hide comment
@tbranyen

tbranyen Dec 14, 2011

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);
Collaborator

tbranyen commented Dec 14, 2011

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

This comment has been minimized.

Show comment
Hide comment
@jashkenas

jashkenas Dec 14, 2011

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.

Owner

jashkenas commented Dec 14, 2011

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

This comment has been minimized.

Show comment
Hide comment
@yuchi

yuchi Dec 14, 2011

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

yuchi commented Dec 14, 2011

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

@jashkenas

This comment has been minimized.

Show comment
Hide comment
@jashkenas

jashkenas Dec 14, 2011

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.

Owner

jashkenas commented Dec 14, 2011

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

This comment has been minimized.

Show comment
Hide comment
@blocka

blocka Dec 14, 2011

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

blocka commented Dec 14, 2011

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

@ehynds

This comment has been minimized.

Show comment
Hide comment
@ehynds

ehynds Dec 14, 2011

+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.

ehynds commented Dec 14, 2011

+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

This comment has been minimized.

Show comment
Hide comment
@yuchi

yuchi Dec 14, 2011

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.

yuchi commented Dec 14, 2011

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

This comment has been minimized.

Show comment
Hide comment
@philfreo

philfreo Jun 14, 2012

Contributor

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.

Contributor

philfreo commented Jun 14, 2012

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

This comment has been minimized.

Show comment
Hide comment
@tonyxiao

tonyxiao Jun 18, 2012

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);
  }
});
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

This comment has been minimized.

Show comment
Hide comment
@braddunbar

braddunbar Jun 28, 2012

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);
};
Collaborator

braddunbar commented Jun 28, 2012

@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

This comment has been minimized.

Show comment
Hide comment
@yuchi

yuchi Jun 28, 2012

What about

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

yuchi commented Jun 28, 2012

What about

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

This comment has been minimized.

Show comment
Hide comment
@theefer

theefer Jul 5, 2012

+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

theefer commented Jul 5, 2012

+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

This comment has been minimized.

Show comment
Hide comment
@dstibrany

dstibrany Jul 6, 2012

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.

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

This comment has been minimized.

Show comment
Hide comment
@sgreenfield

sgreenfield Sep 7, 2012

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

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

@moos

This comment has been minimized.

Show comment
Hide comment
@moos

moos Feb 28, 2013

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

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

moos commented Feb 28, 2013

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

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

@yuchi

This comment has been minimized.

Show comment
Hide comment
@yuchi

yuchi Mar 2, 2013

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 ;)

yuchi commented Mar 2, 2013

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

This comment has been minimized.

Show comment
Hide comment
@mehcode

mehcode Mar 2, 2013

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/

mehcode commented Mar 2, 2013

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

This comment has been minimized.

Show comment
Hide comment
@yuchi

yuchi Mar 2, 2013

Sorry, I forgot to put the good pattern alongside!

yuchi commented Mar 2, 2013

Sorry, I forgot to put the good pattern alongside!

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jan 26, 2015

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

trusktr commented Jan 26, 2015

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

@rochoa rochoa referenced this pull request in CartoDB/carto.js Dec 21, 2015

Merged

🔥 backbone elder #957

@jashkenas jashkenas referenced this pull request in jashkenas/coffeescript Jun 18, 2012

Closed

We Broke Dynamic super #2387

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment