3

I'm used to using this pattern all over my code, and I like it:

var UserWidget = (function(){
    var url = "/users",
        tmpl = "#users li", $tmpl;

    function load() {
        $tmpl = $(tmpl);
        $.getJSON(url, function(json){
            $.each(json, function(i, v) {
                appendUser(v);            
            });
        });
    }

    ...
    return {
        load: load
    };
})();

However, I have many "widget" objects. "ProfileWidget", "PlayerWidget" etc etc. and there's certain actions that each widget share. So ideally, if we're thinking object-orientally, I want each widget object to inherit some methods from a main "Widget" class.

How can I do this without changing this lovely pattern I've been using?

To be more clear I'd like to be able to do something like this:

var Widget = (function() {
    function init() {
        console.log("wow yeah");
    }
})();

// have UserWidget inherit somehow the Widget stuff
var UserWidget = (function() { ...

UserWidget.init(); // -> "wow yeah"
Luca Matteis
  • 29,161
  • 19
  • 114
  • 169
  • 2
    What's the problem with using `new`? TBH, that pattern is far from the best way to use JS's inheritance. – Matt Ball Jun 30 '11 at 13:36
  • @Matt Ball: I don't want to be using `new` because I don't need to instantiate my objects more than once... this is just something I never found out why, but I just don't need to do `new ProfileWidget()` multiple times. – Luca Matteis Jun 30 '11 at 13:39
  • 3
    Have a look a this fantastic answer by bobince http://stackoverflow.com/questions/1595611/how-to-properly-create-a-custom-object-in-javascript/1598077#1598077 – wosis Jun 30 '11 at 13:41
  • 2
    The correct way to do this is with set the prototype property and create a new instance. You don't have to "create multiple instance", just one. I can post an example, but not sure if you interested in the approach. – Glenn Ferrie Jun 30 '11 at 13:55
  • @GlennFerrieLive: definitely I'm interested. Just want some simple syntax to let me do it without re-factoring my existing code base. – Luca Matteis Jun 30 '11 at 13:57
  • @Luca Aren't you going to have to refactor either way? Since you'd be moving common methods to a new base – Allen Rice Jun 30 '11 at 14:22

5 Answers5

3

Keep in mind these solutions are not what I'd typically reccomend and they are just to satisfy the question.

What about closing over everything so that its accessible from your "sub classes" (demo)

var Widget = (function () {

    var init = function () {
        console.log("wow yeah");
    };

    var User = (function () {

        var load = function () {
            init();
        };

        return {
            'load': load
        };
    } ());

    return { 'User': User };
} ());

// Usage: This loads a user and calls init on the "base"
Widget.User.load();

Another way (demo) that you might like is to just use proper inheritance, but within the closure and then return one and only one instance of that new function. This way lets you keep User and whatever else an object

// Closing around widget is completely unneccesarry, but 
//    done here in case you want closures and in case you 
//    dont want another instance of widget
var Widget = (function () {

    // definition that we'll end up assigning to Widget
    function widget() {
           console.log("base ctor");
    }

    // sample method
    widget.prototype.init = function () {
        console.log("wow yeah");
    };

    // put widget in Widget
    return widget;
} ());

var User = (function () {

    function user() { }
    user.prototype = new Widget();

    // TODO: put your User methods into user.prototype

    return new user();
} ());

var Player = (function () {

    function player() { }
    player.prototype = new Widget();

    // TODO: put your Player methods into player.prototype

    return new player();

} ());

User.init();
Player.init();
Allen Rice
  • 19,068
  • 14
  • 83
  • 115
  • That won't actually allow for `UserWidget.init()`. Your code does nothing to associate `UserWidget` with `Widget` or `Widget.init`. – Matt Ball Jun 30 '11 at 13:58
  • Oh, I see what you want now, I've got an answer for that, sec – Allen Rice Jun 30 '11 at 14:00
  • Ok, Updated, all your "base methods" are private and accessible from your "inherited" objects – Allen Rice Jun 30 '11 at 14:09
  • I removed the downvote since it at least works now, but that still _really_ ugly, and more complicated than it needs to be. – Matt Ball Jun 30 '11 at 14:12
  • Of course its way more complicated than it needs to be, I completely agree with you that he should use one of the built in patterns for inheritance. I use that all the time, but if he insists on following this pattern then this is a solution – Allen Rice Jun 30 '11 at 14:14
  • If I'm not wrong, You shouldn't put your methods into player.prototype. Because Widget = player.prototype, you are gonna pollute the Widget. http://jsfiddle.net/Pg8GH/ – Liangliang Zheng Jun 30 '11 at 23:49
  • @Liangliang You're right, I initially had user.prototype = new Widget(); and had Widget being a function. I changed it at the last minute and didnt think about it :) I'll fix the example, thanks! – Allen Rice Jul 01 '11 at 12:26
2

I decided to use Crockford's object:

// function from Douglas Crockford, comments from me
function object(o) {
    // define a new function
    function F() {}
    // set the prototype to be the object we want to inherit 
    F.prototype = o;
    // return a new instance of that function, copying the prototype and allowing us to change it without worrying about modifying the initial object
    return new F();
}

// Usage:
var Widget = (function() {
    function init() {
        console.log("wow yeah");
    }
    return {
        init: init 
    };
})();

var UserWidget = (function() {
    var self = object(Widget); // inherit Widget
    function priv() {}
    self.pub = "boom";
    ...

    return self;
})();

UserWidget.init() // -> "wow yeah"

This works great for me and I like it!

Allen Rice
  • 19,068
  • 14
  • 83
  • 115
Luca Matteis
  • 29,161
  • 19
  • 114
  • 169
  • 6 or one half dozen of another, you're using true prototypical inheritance like everyone suggested :) I like that pattern though! I'm surprised, I hadn't seen that from Crockford yet. (or I did and I forgot about it) +1 for you sir! – Allen Rice Jul 01 '11 at 12:37
0

Without using new, you'll have to use the __proto__ property rather than prototype, so this won't work in all browsers.

var Widget = {
    init: function () {
        console.log("wow yeah");
    }
};

var UserWidget = (function(){
    var url = "/users",
        tmpl = "#users li",
        $tmpl;

    function load() {
        $tmpl = $(tmpl);
        $.getJSON(url, function(json){
            $.each(json, function(i, v) {
                appendUser(v);            
            });
        });
    }

    return {
        load: load
    };
})();

UserWidget.__proto__ = Widget;

UserWidget.init();

Demo: http://jsfiddle.net/mattball/4Xfng/

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
0

You could use Object.create(obj), which I believe is what you're looking for.

Steve Wang
  • 1,814
  • 1
  • 16
  • 12
0

Here's a simple example of prototyping in JS... For more detail on this topic read "JavaScript: The Good Parts"

// widget definition
var Widget = {
    init: function () {
        alert('wow yeah!');
    }
};
// user widget definition
var UserWidget = function () { };
UserWidget.prototype = Widget;
UserWidget.prototype.load = function () { alert('your code goes here'); }

// user widget instance
var uw = new UserWidget();
uw.init(); // wow yeah!
uw.load(); // your code goes here

Hope this helps!

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
  • Thanks but I'm already familiar with this syntax, and it should be `UserWidget.prototype = new Widget()` (you're missing the `new` keyword). However, I'm looking for something that will easily adapt to the patterns I'm using. – Luca Matteis Jun 30 '11 at 14:09
  • OK. I'm sure 'new Widget()' works too. But my code works as is. I tested it before I posted it. – Glenn Ferrie Jun 30 '11 at 14:11
  • @Luca - no missing *new*, Widget isn't a function so can't be used with *new* (it has no [[Construct]] method or prototype property). – RobG Jun 30 '11 at 14:15
  • right, thought `Widget` was a function, my bad. any case, not the syntax I'm looking for. – Luca Matteis Jun 30 '11 at 15:36
  • You shouldn't pollute the UserWidget.prototype, since it is referring to Widget http://jsfiddle.net/Pg8GH/ – Liangliang Zheng Jun 30 '11 at 23:52