0

I thought I had figured out inheritance with JavaScript until this one threw me for a loop.

I have this SuperController as an AMD module:

define([
        'underscore',
        '#allCollections'
    ],

    function ( _ , allCollections) {


        function SuperController() {

        }

        SuperController.prototype = {

            control: function (viewPath, viewOpts, routerOpts, cb) {

            },

            default: function (id) {
                alert('controller has not yet implemented a default route.')
            }
        };


        return new SuperController();

    });

then I have a controller which I would like to inherit from the SuperController:

define(
    [
        'underscore',
        '#allCollections',
        '#allCSS',
        '#SuperController'
    ],

    function (_, allCollections, allCSS, SuperController) {


        function Controller() {

        }

        Controller.prototype = {

            jobs: function (id, changeViewCallback) {

                var viewPath = 'app/js/jsx/relViews/jobs/jobsView';
                var viewOpts = {};
                viewOpts.id = id;
                var routerOpts = {
                    useSidebar: true
                };

                SuperController.control(viewPath, viewOpts, routerOpts, changeViewCallback); // works!
                this.control(viewPath, viewOpts, routerOpts, changeViewCallback); // does *not* work
            },


            default: function (id) {
                console.log('default route implemented in controller.');
            }
        };

        _.extend(Controller.prototype, SuperController.prototype);

        return new Controller();
    });

for some reason I can't this to work - the Controller doesn't have access to control in SuperController. The only way to get it to work is to just call SuperController.control() directly. Is there anything obvious that I am doing wrong?

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817

1 Answers1

1

In the statement _.extend(Controller.prototype, SuperController.prototype);, the identifier SuperController denotes the object that was created by the return new SuperController(); statement in the definition of the SuperController module.

Therefore, SuperController.prototype is undefined. You simply have misunderstood how prototypes work.

Just do not return new SuperController(), instead return the SuperController constructor function from your module definition (because it has the prototype property) and it will work.

I suggest you read one of the many guides and SO answers on JS inheritance, that has been covered a billion times already. Best of luck to you!

Link to an answer

Link to a good guide (oops, it has been modified since the last time I checked it out and now uses ES6 syntax)

Community
  • 1
  • 1
jrsala
  • 1,899
  • 12
  • 13
  • yeah that makes sense..maybe I could do SuperController.constructor.prototype? – Alexander Mills Sep 10 '15 at 20:32
  • No, because you overwrote `SuperController.prototype`! When you create a function, let's say `Foo`, it comes with a `prototype` object that has a `constructor` property which is that function `Foo`: `Foo.prototype.constructor === Foo` is true. However, if you overwrite `Foo.prototype` (which in your case you did) the `.constructor` property is lost. It is not "regained" by instances, so `SuperController.constructor` is again undefined. Naming an instance the same way as the constructor that created it is just EXTREMELY BAD PRACTICE – jrsala Sep 10 '15 at 20:38
  • Also, using the `.constructor` property, in ANY context, is performing ***reflection***. If your program needs reflection, chances are it actually doesn't need it and you just failed at writing software. There is almost ZERO good uses of the `.constructor` property of objects! Just write proper idiomatic JavaScript, for the love of all that is holy! – jrsala Sep 10 '15 at 20:40
  • actually @jrsala the reason why my inheritance scheme was failing was because default is a reserved word in JS. I tried everything and couldn't figure out what was wrong :) I was going a little crazy. I knew about the prototype problem, that wasn't what was wrong. – Alexander Mills Sep 10 '15 at 20:54
  • @AlexMills nope, see [this answer](http://stackoverflow.com/a/17911822/2403587). You can test it in your browser, `var obj = { default: 1 }` just works perfectly fine. – jrsala Sep 10 '15 at 20:58
  • yeah you're right. I actually have the most bizarre situation on my hands. I do exactly as you say, and something still is wrong. the control function is in inherited from the SuperController but for some reason default from the SuperController overrides default in the Controller, not sure why. This is the problem that originally motivated me to ask the question because I originally set up the inheritance scheme as you suggest, but it wasn't working for the reason just stated. – Alexander Mills Sep 10 '15 at 21:05
  • _.defaults is the way to solve this, but this is not my fault, JS is not right – Alexander Mills Sep 10 '15 at 21:08
  • 1
    @AlexMills Of course, your call to `_.extend` overwrites the `default` you just defined in `Controller.prototype` with the one in `SuperController.prototype`... You must perform the extension before, and then override inherited methods: `Controller.prototype = _.extend({}, SuperController.prototype);`. Or you could just use proper JS inheritance with no mixing in: `Controller.prototype = Object.create(SuperController.prototype);` and then `Controller.prototype.default = function () { ... };` – jrsala Sep 10 '15 at 21:08
  • @AlexMills JS is perfectly fine. Just learn the language. – jrsala Sep 10 '15 at 21:09
  • Object.create does mixins under the hood, you know that :) – Alexander Mills Sep 10 '15 at 21:10
  • @AlexMills Now you're just trolling. [RTFM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create). – jrsala Sep 10 '15 at 21:11
  • well, just FYI, _.defaults is the easiest way to get the correct behavior, now you learned something from me – Alexander Mills Sep 10 '15 at 21:12