2

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?

3
  • Please consider using a debugger before getting to SO Commented May 6, 2016 at 16:34
  • 1
    How so? What more would I figure out with a debugger? And what's SO? :p Commented May 10, 2016 at 14:44
  • you could inspect your listener object and see what is it's update property... Commented May 10, 2016 at 14:45

1 Answer 1

3

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.

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

3 Comments

I'm sorry, but I still don't entirely understand, and I can't get it to work by using bind the way you propose, nor in some other ways I tried. So if you want to pass a function to a object, you need to bind everything that function refers to or something like that? I'm a little disappointed in JavaScript then because that's not very nice if you want to use higher-order functions.
This is only an issue if you're trying to pass member functions around on their own, like you're trying to do in setTimeout. setTimeout(function () { model.setSelection(); }, 2000) should work just fine, given what you've pasted.
Ok, thanks I finally got it now! Your solution works indeed!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.