0

I have a nodeJS app requiring player.js. In player.js I define Player and add the method Player.updatePacket, but when I require it in main.js and create a player instance, player.updatePacket is undefined.

player.js:

module.exports.PLAYER_LIST = {};
var Player = exports.Player = {}

//constructor
Player.create = function(id) {
    var tmp = {
        id: id,
        x: 0,
        y: 0
    };
    PLAYER_LIST[id] = tmp;
    return tmp;
}

Player.updatePacket = function() {
    return {
        id: this.id,
        x: this.x,
        y: this.y
    }
}

main.js:

var Player = require('./player.js')

//get called by socket.io when a client connects
//didn't include socket.io setup in example for brevity, but this function
//is called as expected.    
io.sockets.on('connection', function(socket){
    var player = new Player(socket.id)

});

setInterval(function() {
    var dataArr = [];
    for(var i in Player.PLAYER_LIST) {
        var player = Player.PLAYER_LIST[i];
        console.log(player); //this logs: [Function]
        dataArr += player.updatePacket(); //throws TypeError: not a function
    }
    broadcast("update", dataArr);
}, 1000/25);

I have tried moving the export statement to the bottom of player.js and putting updatePacket: function() {/*function contents*/} in the tmp object, and I still get the same error. Any help or explanation is appreciated.

bjubes
  • 195
  • 1
  • 15
  • 1
    there's also an error in that you can't += on an object. Ie, your dataArr is an object and not an array. Also, where is PLAYER_LIST? – derp Mar 01 '17 at 23:17
  • updated OP to show PLAYER_LIST and changed dataArr to an actual array – bjubes Mar 01 '17 at 23:35

2 Answers2

3

Try this

Player.js

var PlayerList = module.exports.PLAYER_LIST = {};
var Player = module.exports.Player = function(id) {
  this.id = id;
  this.x = 0;
  this.y =  0
  PlayerList[id] = this;
};

Player.prototype.updatePacket = function() {
    return {
        id: this.id,
        x: this.x,
        y: this.y
    }
};

Main.js

var Player = require('./player.js').Player;
var PLAYER_LIST = require('./player.js').PLAYER_LIST;

io.sockets.on('connection', function(socket){
    var player = new Player(socket.id)

});

setInterval(function() {
    var dataArr = [];
    for(var i in PLAYER_LIST) {
        var player = PLAYER_LIST[i];
        console.log(player);
        dataArr.push(player.updatePacket()); //note that it is push not += when adding items to an array
    }
    broadcast("update", dataArr);
}, 1000/25);

This explanation might be an oversimplification but in JS, 'classes' are functions and in order to add methods onto classes, we have to add them to the prototype. When we use 'new' on that class, it inherits all methods that exist on its prototype chain.

This link might prove to be a better explanation https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

derp
  • 2,300
  • 13
  • 20
  • Player.prototype is undefined so Player.prototype.updatePacket raises an error. I'm going to try to reimplement this using ES6 class syntax, but this explanation was helpful nevertheless. – bjubes Mar 02 '17 at 00:34
  • thanks for the edit, it turns out that returning another object from the constructor throws havoc with the prototype chaining. https://jsfiddle.net/g6pL9kkt/ – derp Mar 02 '17 at 00:43
2

The exports of your module are an object that have a .Player property:

var Player = exports.Player = {};
//           ^^^^^^^^^^^^^^^^

As such, you need to access that property when you want your Player object:

/* main.js */
var Player = require('./player.js').Player;
//                                 ^^^^^^^

The (preferable) alternative would be to make Player the exports object itself (an "alias"):

/* player.js */
var Player = exports; // or = module.exports;

If you want to use something else than the default object, you cannot use exports but will have to assign to module.exports.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I made the preferred change but I still get the same error. Whats weird is the error changes at random to `TypeError: Player is not a constructor` on the line `var player = new Player(id)`. Just running the same code about five times yields both errors at random – bjubes Mar 01 '17 at 23:28
  • 1
    That's probably a race condition given your asynchronous execution of the functions, whichever happens first will get the exception. But yes, both appear wrong. `Player` is not a constructor - either you want to make it a class or you want to call `Player.create` instead of `new Player`, and `updatePacket` is written to be a static method on `Player` not a method on player objects. You could use `Player.updatePacket.call(player)`, but I really think you're looking for a `class` construct. – Bergi Mar 01 '17 at 23:38
  • thanks that clarifies a lot. I was using the pre ES6 class syntax because I was finding that most examples are written that way. How would I make updatePacket an instance method on a Player, or is that only possible with classes? – bjubes Mar 01 '17 at 23:45
  • @BrianJ See derp's answer for that (not gonna repeat it in the comments here :D) You'd use a constructor function and its `.prototype` object – Bergi Mar 02 '17 at 00:21