Search code examples
javascriptoophigher-order-functionsclass-method

Not a Function TypeError when Calling Object Method in Javascript


Problem:

When I try to call an object method in this particular way in JavaScript, I get the following error:

TypeError: listener.update is not a function

My code:

<html>
<head>
<script src="library.js"></script>
</head>
<body>
<script>

// manages listeners etc.
function Model() {
    var  listeners = [];

    this.addListener = function(listener) {
        listeners.push(listener);
    };

    // the model will call the listeners when "setting the selection"
    this.setSelection = function() {
        for (listener in listeners)
            listener.update();
    };
};

// test function that will be used as update
function moveon() {
    var answer = confirm("Ready to move on?");
    if (answer) window.location = "http://google.com";
}

// create a model and add a listener
var model = new Model();
var listnr = {};
listnr.update = moveon;
model.addListener(listnr);
// update listener
setTimeout(model.setSelection, 2000); // this doesn't work
// setTimeout(listnr.update, 2000);   // but this does

</script>
</body>
</html>

Explanation of the code:

The Model object manages a list of listeners, and calls their update method when some state has changed. In my example, this happens when setSelection is called.

Note:

The error isn't very insightful, and, if I uncomment the last line, listnr.update works fine.

Question:

Why do I get this error when the method is called from the model and/or how can I solve this issue?


Solution

  • model.setSelection doesn't keep the reference to the object. If you don't need to support older browsers you can bind it to the object:

    model.setSelection.bind(model)
    

    If you do need to worry about older browsers, you can just use a small anonymous function:

    function () { model.setSelection(); }
    

    Either method will keep the object reference that is necessary for setSelection to work.

    The reason listnr.update works is because that isn't the same type of function; you built a standalone non-object function and just set a reference to it into that object, so it works just fine. But if you tried that with model, you wouldn't be able to update the object itself.