0

First of all, I'm new to object-oriented JS but fairly experienced with JS and jQuery.

I'm having an issue which makes no sense and is better explained in code, see below:

var $table = $("tbody#peopleContainer"); // hypothetical table to which we will append rows for each person object

var Person = function( name ) { //nothing special here
    this.name = name;
    this.nickname = "";
}
Person.prototype = {
    create: function() {
        $tr = $table.append("<tr></tr>"); //create a blank row

        this.$name = $('<td>'+this.name+'</td>').appendTo( $tr );
        this.$nickname = $('<td><input type="text"></td>').appendTo( $tr );

        $table.append( this.$td ).append( this.$nickname ); 

        self = this;
        this.$name.on("click", $.proxy(self.logName, self)); // logs the name of the person's row you clicked
        $("input", this.$nickname).change(function() { // Should log the nickname you typed as well as the person's name whose nickname you changed
            self.nickname = $(this).val();
            $.proxy(self.logNameAndNickname, self)();  // Problem! Logs the nickname you typed in, but ALWAYS logs the last instantiated person's name
        })
    },
    logName: function() {
        console.log(this.name);
    },
    logNameAndNickname: function() {
        console.log(this.name, this.nickname); // for some reason this.name returns the last instantiated person's name (Person #0).
    }
}

// create 100 people and append to table
x = 100;
while ( x-- > 0 ) {
    person = new Person("Person #"+x);
    person.create();
}

for some reason, logName() logs the right person's name, but logNameAndNickname() always logs the last instantiated person's name, although it logs the correct nickname. It's as if this is referencing 2 separate objects in the same scope.

So my question is - What's going on here?

Followup question: Is this the right way of using jQuery events with objects? I'm new to object oriented JS so please let me know if there's a more appropriate way to accomplish this same thing.

2 Answers 2

1

The problem is that

1) self is a global variable (defined w/o the var keyword) and it gets "overwritten" every time a Person is created.

2) Then you proxy both logName and logNameAndNickname which replaces the this reference in those functions to self which will always refer to the last Person created.

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

2 Comments

Thank you. Can you show how you would rewrite this to work properly?
Well, can't really figure what exactly the code is meant to do and why you need to proxy those functions, but to fix the this reference, just add the var keyword, e.g. var self = this I also recommend not using self for variable name as Gecko based browsers use it as a reference to the window object which may lead to problems.
0

self is an unintentionally global variable, which will be overwritten by each constructor and then hold the last instance after the while loop. You need to make it local to the constructor scope.

Also, if you use $.proxy you do not need to use self, and if you use self you do not need to use $.proxy.

this.$name.on("click", $.proxy(this.logName, this));
/* is equivalent to 
var self = this;
this.$name.on("click", function() {
    self.logName()
}); */

var self = this;
this.$nickname.find("input").change(function() {
    self.nickname = $(this).val();
    self.logNameAndNickname();
});
/* or
this.$nickname.find("input").change($.proxy(function(e) {
    this.nickname = e.target.value;
    this.logNameAndNickname();
}, this)); */

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.