user478419
user478419

Reputation: 413

jquery variable scope problem

I have a chat class with two methods: updateChat and sendChat.

//chat.js
var state;
var room;

function Chat (theRoom) {
    this.update = updateChat;
    this.send = sendChat;
    this.room = theRoom;
}

function updateChat(){    
        alert('ROOM: '+this.room);

        $.ajax({
            type: "POST",
            url: "/chat/process.php",
            data: {
                'function': 'update',
                'state': state,
                'room': this.room
            },
            dataType: "json",
            success: function(data){
                if(data.text){
                    for (var i = 0; i < data.text.length; i++) {
                        $('#chat-area').append($("<p>"+ data.text[i] +"</p>"));
                    }
                }
                if(data.state)
                    state = data.state;
            }
        });
    }
}

//send the message
function sendChat(message, nickname)
{

    alert('A'+state); //20

    //XXX        
    updateChat();

    alert('B'+state); //20

    $.ajax({
        type: "POST",
        url: "/live-event/chat/process.php",
        data: {
            'function': 'send',
            'message': message,
            'nickname': nickname,
            'room': this.room
        },
        dataType: "json",
        success: function(data){

alert('C'+state); //wrong!: 2 //it should be 20!

                    //XXX        
            updateChat();

alert('D'+state); //21

        },
    });
}

The constructor of the chat object:

var chat =  new Chat(4); //4 = the number of the chat room

chat.send('test', 'tester');

My problem are the method calls at the locations marked with XXX. In the updateChat() method, this.room is undefined if I call the updateChat methods like that. But I need to pass the room number to get the right state (state is simply the number of lines in the chat room's text file).

I think it's a problem with variable scope or with the methods not being called in the context of the object.

Upvotes: 0

Views: 130

Answers (2)

Daniel Earwicker
Daniel Earwicker

Reputation: 116654

You may find this easier to grasp if you forget about classes. What you've written there is not a class. There are no classes in JavaScript. You've written a constructor function, but it's of somewhat dubious value because you're assigning the members individually for every instance. The main purpose of constructor functions is to take advantage of prototype-inheritance, so you'd assign the "methods" to the constructor function's prototype.

function Chat (theRoom) {
    this.room = theRoom;
}

Chat.prototype.send = sendChat;
Chat.prototype.update = updateChat;

That way, each object created with new Chat(r) will only need to store the room number, and will not need to store the two methods as properties.

Alternatively, just write a createChatRoom function:

var createChatRoom = function(room) {

    return {
        update: function() {    
            alert('updating: ' + room);
            // ... other stuff
        },

        sending: function() {    
            alert('sending: ' + room);
            // ... other stuff
        }
    };
};

The beauty of that is probably obvious: you don't need to prefix anything with this. The room parameter is in scope to the method definitions, and is also truly private (cannot be modified except through calls to the methods. And the caller doesn't have to remember to put new. They just call the function and get back a fresh object with two methods in it.

You can even safely do this:

setTimeout(chatRoom.update, 10);

The update function "knows" what object it is associated with. It never needs to be told which this to use.

This is so convenient and useful, unless I'm expecting to create very large quantities of objects, I don't bother with constructor functions, new, prototype, etc.

Upvotes: 0

Nick Craver
Nick Craver

Reputation: 630359

You need to maintain this when calling those methods, so instead of this:

updateChat();

You can use .call() to maintain context (so this doesn't revert to window inside the called function), like this:

updateChat.call(this);

Or call the method on the object as @casablanca points out below:

this.update();

There also one more issue, this won't be what you want in your $.ajax() callbacks, it'll be the ajax settings object by default, so you need to set the context option to maintain it, like this:

$.ajax({
  context: this,
  type: "POST",
  //...rest of your current options/methods

Upvotes: 2

Related Questions