Custom Object calling Methods with setTimeout lose

2019-02-06 13:10发布

问题:

I'm having an issue with building a Javascript object, and calling methods within that object using setTimeout. I've tried various workarounds, but always during the second part of my loop the scope becomes the window object rather than my custom object. Warning: I'm pretty new at javascript.

my code:

$(function() {
 slide1 = Object.create(slideItem);
 slide1.setDivs($('#SpotSlideArea'));
 slide1.loc = 'getSpot';
 slide2 = Object.create(slideItem);
 slide2.setDivs($('#ProgSlideArea'));
 slide2.loc = 'getProg';
 slide2.slide = 1;
 setTimeout('triggerSlide(slide1)', slide1.wait);
 setTimeout('triggerSlide(slide2)', slide2.wait);
});

function triggerSlide(slideObject) {
 slideObject.changeSlide(slideObject);
}

var slideItem = {
 div1: null,
 div2: null,
 slide: 0,
 wait: 15000,
 time: 1500,
 loc: null,
 changeSlide: function(self) {
  this.slide ? curDiv = this.div1:curDiv = this.div2;
  $(curDiv).load(location.pathname + "/" + this.loc, this.slideGo(self));
 },
 setDivs: function(div) {
  var subDivs = $(div).children();
  this.div1 = subDivs[0];
  this.div2 = subDivs[1];
 },
 slideGo: function(self) {
  if(this.slide) {
   $(this.div2).animate({
    marginLeft: "-300px"
   }, this.time);
   $(this.div1).animate({
    marginLeft: "0"
   }, this.time);
   setTimeout('triggerSlide(self)', this.wait);
  } else {    
   $(this.div1).animate({
    marginLeft: "300px"
   }, this.time);
   $(this.div2).animate({
    marginLeft: "0"
   }, this.time);
   setTimeout('triggerSlide(self)', this.wait);
  }   
  this.slide ? this.slide=0:this.slide=1;
 }
}

My latest attempt was building the helper function triggerSlide so that I could attempt to pass the reference to the object through my methods, but even that doesn't seem to work.

I could use setInterval and it works, however:

  1. I want to ensure the animation has completed before the timer restarts
  2. I don't learn how to get around the issue that way. :)

回答1:

This should be required reading for Javascript programmers getting started with the language (and with Stackoverflow).

Unlike Java, there's no intrinsic "binding" of functions to any object, regardless of how they're declared. The binding of object context happens only at function invocation time. Thus, when you pass a reference to a function to something like "setTimeout", it doesn't matter at all that you got the function from some object. It's just a function, and "setTimeout" will call it with the default context — the window object.

There are many ways to handle this (and I won't call it a "problem"; it's a fact of the language, and a very powerful one). If you were using a framework of some kind it'd be easier.

Another thing: you're binding your timeout and interval handlers as strings containing code. That's a pretty ugly practice, so you should get used to forming function expressions — that is, expressions whose values are functions.

For your "slideItem" handler, for example, you might do this:

// ...
setTimeout(function() { slideItem.changeSlide(); }, 5000);

That way, the function called by the timeout mechanism will always invoke "changeSlide" with your "slideItem" object as the context. You don't need that "self" parameter in "changeSlide" because "this" will point to the right object.

[edit] I note that you are in fact making some use of jQuery, which is a good thing.



回答2:

This is jQuery, correct? There's a callback parameter to animate(); pass it the function you want called after the animation completes and jQuery will take care of calling it. The callback should capture the scope you want just fine.



回答3:

I can't test this code right now, but does this help?

setTimeout(function(){triggerSlide(slide1)}, slide1.wait);