zoukankan      html  css  js  c++  java
  • 如何用setInterval调用类的方法

    setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。

    近日在编写程序时发现调用setInterval传入的方法时,在程序间隔调用时无法获取正确的类方法,究其原因是由于JS灵活的this指针重绑定导致的。那么this指针为什么会重绑定,又重绑定到哪里去了呢?setInterval又做了些什么 工作呢?

    大家都知道JS的类是通过function模拟出来的,并不是真正面向对象的类,那么就要从这里说起了。JS模拟类的方式大致分为3中形式:第一种是HW开发人员惯用的,原型扩展方式模拟类;第二种是匿名对象扩展模拟类;第三种就是通过Function动态方式创建类,这种方式可mapping到面向对象语言中的反射机制,有异曲同工之处,当然今天要讲的重点不是这个,回到我们的主题。要了解this指针,首先我们来看个例子:

    function getName(){
        return this.name;
    }
    function Person(){
         this.name = "sparon";
    }
    Person.prototype.sayName = getName;
    var people = new Person();
    people.sayName();
    result:
    sparon
    

    这个例子说明this指针是晚绑定的,而并不是程序初始化时绑定的。在《JAVASCRIPT高级程序设计》一书中"7.2.2关于this对象" 一节中指出:"this对象是在运行时给予函数的执行环境绑定的"。this作为全局唯一特殊指针存在,this作为类内部调用标志,当我们处于类外部调用this指针访问类内部成员时是无效的。

    好了,在明确这点后我们在回过头来讲讲setInterval这个函数,其实准确的说setInterval是widnow对象的一个方法,window对象也是一个特殊的对象,以后有机会再慢慢讲来。

    语法:setInterval(code,millisec[,"lang"])

    • code          :        必需。要调用的函数或要执行的代码串。
    • millisec:   必须。周期性执行或调用 code 之间的时间间隔,以毫秒计。

    后面的参数基本用不到,这里就不作解释了。

    在这里code可以是一个函数名,可以是函数名字符串,同时也可以是一段代码,或一个匿名函数,这就是JS语言的灵活之处。milisec当然就是调用间隔时间了,下面就来说说这个code参数。

    先来看看我早起的调用,大家看看问题出在哪里,并想一下在gameStart 和gameLoop中的this指针分别是指向那个对象,是不是同一个对象,如果不是为什么。

    function SPARONGame(){
        this.name = "DotaAllStar";
        this.player_Num = 10;
        this.game_init = false;
        this.game_start = false;
        this.game_speed = 100;
    }
    SPARONGame.prototype.init = function() {
        // Todo init game...
        $("#start").click(this.gameStart);
        this.game_init = true;
    }
    SPARONGame.prototype.gameStart = function() {
        if (!this.game_init) { // 这里调用this.game_init会存在问题,大家想想问题出在哪里?这里this又指向何处?
            return;
        }
        this.game_start = true;
        setInterval(this.gameLoop, this.game_speed);
    }
    SPARONGame.prototype.gameLoop = function() {
        if (!this.game_start){ // 这里调用this.game_start会存在问题,大家想想问题出在哪里?这里this又指向何处?
            return;
        }
    }
    

    在这段代码中最关键的问题在于作用域问题,要解决这个问题首先要了解this指针是指向何处?首先gameStart中的this是指向document对象的,因为当执行$("#start").click(this.gameStart);时其实click是将gameStart作为函数来处理了,所以当用户点击页面的start标签时,JS将gameStart函数绑定到了document对象下。而gameLoop中的this是指向window的,原理相同,至于为什么,这就要回顾到我前面的示例了,在之前讲过this指针是动态晚绑定的。

    那么在知道这点后,我怎么来修正呢?这里就要用到JS的命名空间了,正是由于this是晚绑定的,而在gameStart和gameLoop中将对象方法传递给事件处理函数时,JS无法动态的去解析或者说变更this为指向的对象,从而导致了在后期调用该参数是,this被重绑定。这里有很多解决方案,之前在网上看到一篇文章是将this对象通过eval函数设置到window对象下,这样全局可访问,到调用方法内部再将其取回即可正常使用,还有一种方法很简单,就是利用变量存储指针引用,然后在匿名函数中通过变量调用方法,第三种就是我所讲到的解决方案,通过命名空间定位。

    在JS中点(.)通常表示类.方法/字段/…,但可以从另一方面来理解,点操作符时间上是通过命名空间的方式模拟除了面向对象的功能,我们其实可以将点操作符作为命名空间的路径标志,这样,当我们传送一个具体实例类的时候JS解释器就能够方便的找到我们所要的方法,并且知道该方法在具体那个实例下,好了,下面我们来体会一下,我将上面的例子稍作变动即可成功。

    var SPARONGame = {
        name : "DotaAllStar",
        player_Num : 10,
        game_init : false,
        game_start : false,
        game_speed : 100,
        init : function() {
            // Todo init game...
            $("#start").click(this.gameStart);
            this.game_init = true;
        },
        gameStart : function(){
            if (!SPARONGame.game_init) {
                return;
            }
            SPARONGame.game_start = true;
            setInterval("SPARONGame.gameLoop()", SPARONGame.game_speed);
        },
        gameLoop : function() {
            if (!this.game_start){
                return;
            }
        }
    }
    

    在这段代码中需要注意的是setInterval的第一个参数是字符串,而不是类名.方法名,否则最后达到的效果仍然是将其作为函数传入。

    在解决以上问题后,最后再简单的提下setInterval中的两个小BUG,虽然很微妙,但是一定要知道,否则在编写程序处理边界问题时就会出现大问题了。1.某些间隔会被跳过;2.多个定时器的代码执行之间的间隔可能会比预期的小,关于这两个问题的详细解答可参考《JavaScript高级程序设计》的"18.2.1 重复的定时器"一节。

  • 相关阅读:
    springboot 集成jsp
    eclipse 创建 springboot项目
    eclipse 导入别人拷贝过来的工作空间项目
    vue安装及使用
    eclipse配置svn导出项目
    sql为什么用0,1表示男女?在sql语句里转好还是在页面转好?
    svn下载多模块及依赖框架的项目
    django连接sqlserver
    字符编码
    数组希尔排序法
  • 原文地址:https://www.cnblogs.com/xiaoyang002/p/4039415.html
Copyright © 2011-2022 走看看