views:

197

answers:

4

I have this class where I am using a combination of jQuery and prototype:

var MyClass = Class.create({
 initElements: function(sumEl) {
 this.sumEl = sumEl;
 sumEl.keyup(this.updateSumHandler);
 },

 updateSumHandler: function(event) {
    // Throws error here: "this.updateSum is not a function"
 this.updateSum();
 },

 updateSum: function() {
 // does something here
 }
});

How can I all this.updateSum() after all?

+5  A: 

You need to use closures.

 initElements: function(sumEl) {
        this.sumEl = sumEl;
        var ref = this;
        sumEl.keyup( function(){ref.updateSumHandler();});
 },
epascarello
A: 

It is the famous Javascript idiom you need to use in initElements function:

var that = this;

Later in your handler just refer to that instead of this:

var MyClass = Class.create({
    initElements: function(sumEl) {
        this.sumEl = sumEl;
        var that = this;
        sumEl.keyup(this.updateSumHandler);
    },
    updateSumHandler: function(event) {
        that.updateSum();
    },
    updateSum: function() {
        // does something here
    }
});

It was covered in great detail in talk by Stuart Langridge on Javascript closures at Fronteers 2008 conference.

Michał Rudnicki
Your `that` is not visible inside `updateSumHandler`.
Crescent Fresh
No, why? It's a closure so it should be in scope inside updateSumHandler. Didn't test that code, though.
Michał Rudnicki
+4  A: 

Totally untested suggestion:

sumEl.keyup(this.updateSumHandler.bind(this));

.bind() gives back a new function where the first parameter of bind is closured for you as the function's this context. It can also closure parameters check out the documentation

Function.bind() to me is the single best function ever written in javascript :)

Martijn Laarman
+1. `Function.bind` will be a standard language feature in a future version of JavaScript.
bobince
Really ? Can you link me ?
Martijn Laarman
http://wiki.ecmascript.org/doku.php?id=es3.1:es3.1_proposal_working_draft
bobince
+1  A: 

DOMEvent handlers are traditionally called with the elements they're registered to as context / "this". This is what jQuery does, too.

The easiest option for you would be to use jQuery's ability to handle event data

var MyClass = Class.create({
 initElements: function(sumEl) {
        this.sumEl = sumEl;
        sumEl.bind("keyup", this, this.updateSumHandler);
 },

 updateSumHandler: function(event) {
    // event.data is the initial this

    // call updateSum with correct context
    event.data.updateSum.call(event.data);
 },

 updateSum: function() {
        // does something here
 }
});

The other possibility is to use closures to define the updateHandler inside the constructor

var MyClass = Class.create({
 initElements: function(sumEl) {
        this.sumEl = sumEl;

        // save this as that so we can access it from the anonymous function
        var that = this;
        sumEl.keyup(function()
        {
           that.updateSum();
        });
 },

 updateSum: function() {
        // does something here
 }
});

This is a working example what one of the other answers tried to do. It works because the anonymous function can always access the variables in the surrounding function -- but it only works if the function is really defined in the function that has "that" as local variable.

fforw