2
votes

I'm creating an Javascript "class" with prototyping. I don't understand why the first/second block won't work, and the third block will work. For the first/second block I get: "Object # has no method 'validate' ". Why does it do that, and is block 3 the correct way?

--Edit I have tested this in Chrome/FF

--Edit2 If I call the Test prototype with: var test = new Test();

And call the test var in de login prototype it will work....

Block 1


    function Test(){
        this.init();
    }

    Test.prototype.init = function(){
        $(".login").click(this.login);
    };

    Test.prototype.login = function(event){
        event.preventDefault();

        this.validate();

        console.log("login");
    };

    Test.prototype.validate = function(){
        console.log("validate");
    };

new Test();

Block 2


    function Test(){
        this.init();
    }

    Test.prototype.init = function(){
        $(".login").click(this.login);
    };

    Test.prototype.login = function(event){
        var self = this;
        event.preventDefault();

        self.validate();

        console.log("login");
    };

    Test.prototype.validate = function(){
        console.log("validate");
    };

    new Test();

Block 3


    function Test(){

    if(!(this instanceof LoginController)){
        return new LoginController();
    }

        self = this;

        this.init();
    }

    Test.prototype.init = function(){
        $(".login").click(this.login);
    };

    Test.prototype.login = function(event){
        event.preventDefault();

        self.validate();

        console.log("login");
    };

    Test.prototype.validate = function(){
        console.log("validate");
    };

    new Test();

1
All your three blocks works properly in my javascript console. Also note that in your block 3 you are overwriting the global variable self, which is never a good idea. I'd say that block 1 is the correct way to go. - Matteo Tassinari

1 Answers

2
votes
Test.prototype.init = function(){
  $(".login").click(this.login);
};

Here you're attaching this.login as event handler. Done this way, the function will have it's this value reassigned by jquery to the element that triggered the event.

To to keep a reference to the this value you actually want, try:

Test.prototype.init = function(){
  var self = this;
  $(".login").click(function (evt) {
     return self.login(evt);
  });
};

or for browsers implementing bind:

Test.prototype.init = function(){
  $(".login").click(this.login.bind(this));
};

or jQuery.proxy, which does the same thing as bind:

Test.prototype.init = function(){
  $(".login").click($.proxy(this.login, this));
};

demo: http://jsbin.com/ilejiw/1/


Update: No, the 3rd variant is not the correct way, as every instance of Test would overwrite the same global self variable. So self would point to the last instance of Test (as long as it has not been overwritten by something else).