Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript Array Member Variable is Undefined with Prototype method

In the code below, the pushElement method works just fine when dealing with the "words" variable, but as soon as I run the popElement method, it fails on the "this.words.length" piece with the following error: "Uncaught TypeError: Cannot read property 'length' of undefined".

Any ideas?

function AnimationStack() {
    this.words = [];
}

AnimationStack.prototype.pushElement = function(element) {
    this.words.push(element);
}

AnimationStack.prototype.popElement = function() {
    if (this.words.length>0) {
        var element = this.words.shift();
        return element;
    } else {
        return null;
    }
}

var AS = new AnimationStack();

var element = $("<div></div>");
AS.pushElement(element); // works perfect
AS.pushElement(element); // works perfect
AS.pushElement(element); // works perfect

var pop = AS.popElement(); // always fails

EDIT: The code above is perfect. It was in my actual implementation of how I was using the code above. I'm using setInterval to call popElement() which changes the scope of "this". Read the full answer here:

http://forrst.com/posts/Javascript_Array_Member_Variable_is_Undefined_wi-g6V

like image 771
Chad Hutchins Avatar asked Nov 15 '22 02:11

Chad Hutchins


1 Answers

@Chad already found the answer, but here is the explanation.

If you call the function like this:

AS.popElement();

the popElement function runs in the context of the AS object (meaning "this" refers to AS). But if you use setInterval (or any callback-style function) like this:

setInterval(AS.popElement, 1000);

you are only passing a reference to the popElement function. So when popElement is executed 1000 milliseconds later, it is executed in the global context (meaning "this" refers to window). You would get the same error if you called:

window.popElement();

A possible alternative to avoid this is to do the following:

setInterval(function() { return AS.popElement() }, 1000);

Another option could be to use the apply or call methods to set your context explicitly:

setInterval(AS.popElement.apply(AS), 1000);
like image 168
Adam Roderick Avatar answered Dec 05 '22 06:12

Adam Roderick