简体   繁体   中英

JavaScript Recursive Callback Function - ReferenceError: function is not defined

My idea is to show all images in the array. At first the Noise image is getting hidden after this it calls the showArray() function to show the images in the Array.

Trial = (function() {
  function Trial(imageArray) {
    this._images = new Array();
    this._images = imageArray;
  }

  Trial.prototype.startTrial = function() {
    return $("#noise").hide(0, showImages());
  };

  Trial.prototype.showImages = function() {
    var imageToShow;
    imageToShow = this._images.pop();
    if (imageToShow === void 0) {
      $("#noise").show;
      return;
    }
    $("#img").attr("src", imageToShow.src);
    return $("#img").fadeIn(0).delay(imageToShow.delay).fadeOut(0, showImages());
  };

  return Trial;

})();

image1 = new AMPImage("someImage1.png", 500);  //imagePath, delay in ms
image2 = new AMPImage("someImage2.png", 750);
image3 = new AMPImage("someImage3.png", 1000);

myImageArray = [image1, image2, image3];
trial1 = new Trial(myImageArray);
trial1.startTrial();

But on executing startTrial() I get the error message

ReferenceError: showArray is not defined

Based on your latest edit:

The context doesn't work quite like that. ie showImages isn't a variable. It's a method on the prototype of the Trial class. If you want to call it from external code you can do:

myTrial.showImages();

If you want to call it from another method within the Trial class, you have to call it as a method on the current instance. You can get a reference to the current instance as this . ie

this.showImages();

instead of

showImages();

What you actually want to do is not call the function immediately though, you want the function to be called once the animation finishes. To do this you pass the function as a value to the animation method. Just omit the () and you will pass the function.

The problem with this is that the this context is a fickle beast, and will not be correct if you do that. To maintain the correct this context you must bind it:

var showImagesToCallLater = this.showImages.bind(this);

// some time later, perhaps inside another method you can then do:

showImagesToCallLater();

// and this will have the correct `this` context.

So for our example we want .hide(0, this.showImages.bind(this)); and .fadeOut(0, this.showImages.bind(this));

Additional Code Review

I've reviewed the rest of your code, suggesting additional changes you might like to make. If you just want to get the code working as quickly as possible, you can probably ignore this:

// There's no need to create an extra closure here,
// we can remove a level of nesting.
function Trial(imageArray) {
  // There's no point writing to `this._images` then immediately
  // overwriting, so we only write to it once here
  this._images = imageArray;
}

Trial.prototype.startTrial = function() {
  // I assume you want to begin showing images once #noise is hidden.
  // To do this, pass a function to hide.
  return $("#noise").hide(0, this.showImages.bind(this));
};

Trial.prototype.showImages = function () {
  var imageToShow;
  imageToShow = this._images.pop();
  if (imageToShow === void 0) {
    // You need to actually call this function
    $("#noise").show();
    return;
  }
  $("#img").attr("src", imageToShow.src);
  // Once one image has been shown, I'm guessing you want to show the next.
  // to do this, we `.bind` the function so it has the correct `this` value.
  return $("#img").fadeIn(0).delay(imageToShow.delay)
                  .fadeOut(0, this.showImages.bind(this));
};

image1 = new AMPImage("someImage1.png", 500);  //imagePath, delay in ms
image2 = new AMPImage("someImage2.png", 750);
image3 = new AMPImage("someImage3.png", 1000);

myImageArray = [image1, image2, image3];
trial1 = new Trial(myImageArray);
trial1.startTrial();

Rather than calling the function, which will make it execute immediately, you want it to execute when the animation has finished. To do this, you need to pass the actual function, so call .bind(this) on the function. That way it will execute in the correct context.

You could alternatively build a more "functional" version of this as:

function startTrial(imageArray) {
  // note how we pass the function showImage, we don't call it
  $("#noise").hide(0, showImage);
  function showImage() {
    var imageToShow = imageArray.pop();
    if (imageToShow === undefined) {
      $("#noise").show();
      return;
    }
    $("#img").attr("src", imageToShow.src);
    return $("#img").fadeIn(0).delay(imageToShow.delay).fadeOut(0, showImage);
  }
}
var image1 = new AMPImage("someImage1.png", 500);  //imagePath, delay in ms
var image2 = new AMPImage("someImage2.png", 750);
var image3 = new AMPImage("someImage3.png", 1000);

var myImageArray = [image1, image2, image3];
startTrial(myImageArray);

Which style you prefer is very much up to personal taste. The functional version lets you forget about all the strange this binding behavior.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM