1

I want to achieve this:

var player = new Player (socket, x, y)
player.on ('event1', function () {});
player.emit ('event2', data);

where socket is an instance of Socket supplied externally.

So basically I need to inherit Player from Socket, but initialize it from Socket's instance:

var Player = function (socket, x, y)
{
    .....?
    this.x = x;
    this.y = y;
}

Any idea how I to do it the simplest and the most elegant way?

3 Answers 3

3

From your question, I assume you've considered but rejected using composition:

var Player = function (socket, x, y)
{
    this.socket = socket;
    this.x = x;
    this.y = y;
};

Usage:

var player = new Player(socket, x, y);
player.socket.on(/*...*/);
player.socket.emit(/*...*/);

FWIW, I would probably use composition.

But if you don't want to, you have at least a couple of choices:

  1. Really inherit, using socket as the prototype of the new object:

    In this scenario, you probably wouldn't use new:

    var Player = function (socket, x, y)
    {
        var obj = Object.create(socket);
        obj.x = x;
        obj.y = y;
        return obj;
    };
    

    Usage:

    var player = Player (socket, x, y); // No `new` here, although it's harmless
    player.on ('event1', function () {});
    player.emit ('event2', function () {});
    

    (Although if you did use new, it would be harmless — the object created by new would just be thrown away, because Player returns a non-null object reference, and so overrides the default result of new.)

    This won't work if you're relying on the objects inheriting Player.prototype, however. You could copy everything from Player.prototype onto the instance by adding this code to the above:

    var key;
    for (key in Player.prototype) {
        if (Player.prototype[key] !== Object.prototype[key]) {
            this[key] = Player.prototype[key];
        }
    }
    

    That copies any properties from Player.prototype (and its prototypes, except for Object.prototype as the player will already have those) onto the player object. Less than ideal, but functional, it just means that you need to make sure that Player.prototype has everything on it before you create the first player. Also, instanceof won't work for players.

  2. Just wire up the bits you want, without actually using socket as the prototype, by adding bound functions to your Player instance that actually route to socket:

    var Player = function (socket, x, y)
    {
        this.on = socket.on.bind(socket);
        this.emit = socket.emit.bind(socket);
        this.x = x;
        this.y = y;
    };
    

    Usage is then as in your question, using new.

    Or a bit more verbosely, but giving you the opportunity to get in the middle if you needed:

    var Player = function (socket, x, y)
    {
        this.socket = socket;
        this.x = x;
        this.y = y;
    };
    Player.prototype.on = function() {
        return this.socket.on.apply(this.socket, arguments);
    };
    Player.prototype.emit = function() {
        return this.socket.emit.apply(this.socket, arguments);
    };
    

Both of the above use features from ES5 that can be polyfilled. From Socket, on, and emit, I'm guessing you're using NodeJS, and so you're using V8 and you have those features. If not, though, and for others with similar questions: The first solution uses the single-argument version of Object.create, the second uses Function#bind. If you need to support older JavaScript engines, just look up polyfills for them, both are quite small.

Sign up to request clarification or add additional context in comments.

7 Comments

And also.. 3. Composition: this.socket = socket; and then player.socket.on('event1', function () {}'). 4. Mixin/extend.
@dfsq: Indeed. I assumed that the OP had considered and rejected composition, but I should have flagged it up (it would be my first choice). I'll do that. I probably wouldn't use mixin here, too much opportunity for stomping on things as Socket is defined by code outside the OP's control.
Thanks for detailed answer. The first solution seems to work, but when adding custom functionality through Player.prototype.foo = function () {...} and calling player.foo() it leads to player.foo being undefined! The second solution works flawlessly so I'll take it. Thanks!
I've rejected player.socket.on(...) composition because it looked not that elegant for my taste. I wanted my players to look like they emit and receive events directly.
@VincentPride: Yeah, I'd just added a note about Player.prototype to the answer a few minutes before your comment, you might not have seen it. :-) Glad the second option works! :-)
|
2

So basically I need to inherit Player from Socket

No you don't. Inheritance is an is-a relationship and a Player is probably not a Socket. It probably does on the other hand use a Socket instance - and that type of relationship can be modelled by a composite relationship.

So you need to inject the Socket instance into your Player constructor for use in the resulting Player instance player.

What you are attempting to do here is use inheritance for code-reuse and that has been shown over the years to be a flawed approach because of the tight implicit (and usually unwanted) coupling it introduces between parent and child object. Based on the information in your question, composition is the way to go.

2 Comments

You're right, composition would be more cool in my case, since I'm using only on() and emit(). But imagine having a huge superclass with dozens of methods which you have to rebind in your child class.
@VincentPride JS doesn't have classes (yet) - although I know what you mean. But the bit about a huge object and rebinding, I don't follow.
0

The term inheritance is different in JS as compared to other languages. Specifically, when many people think of inheritance, they think of classical inheritance which doesn't exist in JS. Instead, JS uses the concept of delegation to prototype objects in order to simulate inheritance-like behavior. Based on what you're describing, it seems like you want instances of Player to have functionality that belongs to Socket. In that case, what you're describing is more like instances of Player delegating to Socket.

Here's some example code:

// assuming you have code that defines what a socket is
var Player = function (x, y) {
  var socket = new Socket();
  var p = Object.create( socket );
  p.x = x;
  p.y = y;

  return p;
};

Instances of Player will now be able to delegate property lookups to the socket instance.

6 Comments

ECMA-262 4.2.1: "Each constructor is a function that has a property named prototype that is used to implement prototype-based inheritance"
@T.J.Crowder after having read github.com/getify/You-Dont-Know-JS/blob/master/…, I'm also of the author's opinion that the term prototypical inheritance is a misnomer. In classical inheritance, when a class inherits from another class, those methods and properties are copied over into the new subclass. However, the dynamic is JS is completely different. Properties are looked up on the instance and if not found, the lookup is delegated to the prototype object.
@wmock: Use of the word "inheritance" with prototypical inheritance is very well-established with something like a 35-year history, long predating JavaScript. Feel free to race against the wind if you like, but I'd keep it out of answers if you're trying to be most useful. :-) (Not my dv)
@T.J.Crowder I think we're both in agreement in terms of how this question should be solved :) Now it's just a matter of whether we both agree with the semantics of the terminology used - to each their own!
The spec uses the term "prototypical inheritance". I DVd because this means that you need to supply significant evidence to back-up your assertion that "There is no such thing as inheritance in JS". If you said: "There is no such thing as classical inheritance in JS", then fair enough :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.