asimes
asimes

Reputation: 5894

Why can't function in JavaScript "Class" call itself?

I can't figure out what is wrong with this code. Firefox's error console tells me: "this.animateImgSlider is not a function".

What I would like is to call this.selectImage() with jsItems[0].selectImage(0) and then have this.animateImgSlider() be called some number of times until selfCall is false:

function WindowItem(inId) {
    this.id = inId;
    this.imgSliderTarget = 0;
    this.imgSlider = document.getElementById("imgSlider"+inId);

    this.animateImgSlider = function() {
        var selfCall = true;
        var currLeft = parseInt(this.imgSlider.style.left, 10);
        if (currLeft < this.imgSliderTarget) {
            currLeft += 8;
            if (currLeft >= this.imgSliderTarget) {
                currLeft = this.imgSliderTarget;
                selfCall = false;
            }
        }
        else {
            currLeft -= 8;
            if (currLeft <= this.imgSliderTarget) {
                currLeft = this.imgSliderTarget;
                selfCall = false;
            }
        }
        this.imgSlider.style.left = currLeft+"px";
        if (selfCall) setTimeout("this.animateImgSlider()", 0);
    }

    this.selectImage = function(inImg) {
        this.imgSliderTarget = -inImg*488;
        this.animateImgSlider();
    }
}
var jsItems = new Array();
jsItems.push(new WindowItem(0));

This line is the one that throws the error:

if (selfCall) setTimeout("this.animateImgSlider()", 0);

Upvotes: 2

Views: 3624

Answers (2)

GriffLab
GriffLab

Reputation: 2166

You can use bind .here is the link

if (selfCall) {
    setTimeout(function () {
        this.animateImgSlider();
    }.bind(this), 0);
}

Upvotes: 2

Ian
Ian

Reputation: 50905

Use this instead:

if (selfCall) {
    var self = this;
    setTimeout(function () {
        self.animateImgSlider();
    }, 0);
}

(of course, it would make more sense to me to declare self at the top and use that throughout the function instead of this)

A string that is passed to setTimeout to execute is done so in the global scope, meaning this refers to window. It is not executed in the current scope, where the setTimeout call is.

On a side note, as a suggestion, I would move your animateImgSlider and selectImage methods outside, and into the WindowItem prototype, so that they can be shared by all instances, instead of creating a new anonymous function each time. So for example, you'd have:

function WindowItem(inId) {
    // blah blah
}

WindowItem.prototype.animateImgSlider = function () {
    // blah blah
};

WindowItem.prototype.selectImage = function(inImg) {
    // blah blah
};

The value of this will still refer to the specific instance, unless of course you attempt to use it in a string for the setTimeout parameter :)

This is clearly not required, as your code currently works fine, but it is a commonly used convention for declaring methods on a constructor function for instances to share.

Upvotes: 5

Related Questions