修改setInterval作用域

Hello,今天和大家分享如何修改setInterval作用域。

0.引子

  最近在做一个项目的时候需要开发一个图片轮播显示的组件,在实现过程中遇到了关于setInterval作用域的问题。

  

 1 SlidePicture.prototype={
 2     constructor:SlidePicture,
 3     init:function(){        //初始化函数
 4         this.autoPlay();
 5     },
 6     slidePic:function(){    //滑动图片具体实现
 7         //省略实现代码
 8         console.log('duration-->'+this.duration);
 9         console.log('delay-->'+this.delay);
10     },
11     autoPlay:function(){    //自动轮播图片
12         setInterval(this.slidePic,this.delay);
13     }
14 }
15 //构造函数
16 function SlidePicture(delay,duration){
17     this.delay=delay;
18     this.duration=duration;
19     this.init();
20 }
21 
22 var sp=new SlidePicture(5000,800);

  在上面的代码中,我们自定义并扩展一个SlidePicture原型。主要总用就是,我们在创建原型对象时会调用window的setInterval方法,

  来定时调用图片切换方法,从而实现自动图片的自动轮播效果。

  slidePic方法中具体的实现方法我先省略了(下次可以继续和大家分享这个完整的组件),只是在这里打印了对象中的两个属性值,

  结果却出现以下信息:

  

  都是undefined,为什么呢?

  很明显问题不是出在SlidePicture对象上,因为我们已经给这个对象初始化过属性值了。

  打开firebug脚本调试,在第8行打上断点:

  

  终于发现了问题所在:slidePic方法中的this居然是指向window而不是当前的SlidePicture对象。

  

1.问题

  我们重新审查代码,结果把关注点提到第12行来:

setInterval(this.slidePic,this.delay);

  问题来了,setInterval以及setTimeout这些方法都是全局作用域window的函数,

  所以上面的代码等价于:

1 setInterval(function(){
2             console.log('duration-->'+this.duration);
3             console.log('delay-->'+this.delay);
4         },this.delay);

  那么问题就清楚了,函数实现中的this当然是指向window了,千万别被this.slidePic蒙骗。

  既然this就是window,那么delay,duration这两个属性当然也就不存在了。

 

2.apply/call?

  既然是作用域的问题,那么解决问题的关键也就是如何修改setInterval的作用域了。

   修改函数作用域,我们的第一个方案当然就是apply/call了,我们按照理论尝试将代码修改如下:

autoPlay:function(){    //自动轮播图片
        var self=this;
        setInterval(self.slidePic.apply(self),this.delay);
    }

  我们将当前对象this传递给apply方法,看起来应该是OK的,运行后结果如下:

  

  果然,结果出来了。

  等等,好像有点不对!这个结果只打印了一次,并不是根据setInterval定时循环调用的。

  换成了call结果也一样。

  查了查这两个方法:apply/call如果用在setInterval中的话,只会立即运行一次,而不会按照我们的设想循环调用。

  怎么办?

  我们的主角出场了:bind。

  call和apply是立即执行方法,而bind是产生一个新方法用于后续调用。

  我们修改了下代码:

setInterval(self.slidePic.bind(self),this.delay);

  果然我们想要的结果出现了:

  

 

  

3.匿名函数

  还有没有别的办法?

  其实在一开始的时候我们已经说了,给setInterval传递this.slidePic参数等价于:

1 setInterval(function(){
2             console.log('duration-->'+this.duration);
3             console.log('delay-->'+this.delay);
4         },this.delay);

  这里我们定义了一个匿名函数作为参数setInterval的第一个参数,但是忽略了一点,

  这个匿名函数中的this仍然指向window,所以这种做法当然也是不行。

  那么我们只要将this换成我们的SlidePicture对象就可以了。

  调整下代码:

autoPlay:function(){    //自动轮播图片
        var self=this;
//        setInterval(self.slidePic.bind(self),this.delay);
        setInterval(function(){
            self.slidePic();
        },this.delay);
    }

  在调用setInterval之前我们先将当前对象this保存下来,然后在匿名函数中调用了就OK了!

  这也是处理this对象的一种常见方案。


 

   ------写得乱七八糟的,不管怎样希望对各位有所帮助-----

 

 

 

 

  

 

posted @ 2015-11-19 13:44  大魔王萨格拉斯  阅读(3693)  评论(2编辑  收藏  举报