如何用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 重复的定时器"一节。
如何用setInterval调用类的方法的更多相关文章
- Java动态调用类中方法
在Java中,调用类的方法有两种方式:对于静态方法可以直接使用类名调用,对于非静态方法必须使用类的对象调用.反射机制提供了比较另类的调用方式,可以根据需要指定要调用的方法,而不必在编程时确定.调用的方 ...
- C#通过反射调用类及方法
反射有个典型的应用,就是菜单的动态加载,原理就是通过反射调用某个窗体(类).下面演示一下通过反射调用类及方法: 1.新建一个类,命名为:ReflectionHelper,代码如下: #region 创 ...
- Spring事务调用类自己方法失效解决办法和原因
问题 正常情况下,我们都是在controller里调用service里的方法,这个方法如果需要加事务,就在方法上加上@Transactional,这样是没问题的,事务会生效. 可是如果像下面这样,绕以 ...
- C# 如何利用反射,将字符串转化为类名并调用类中方法
首先,先随便创建一个测试类 <span style="font-family:Microsoft YaHei;font-size:18px;">public class ...
- PHP使用字符串名称调用类的方法
<?php class Game { function Play($id) { echo "Playing game $id\n"; } } $game = new Game ...
- rutime中动态调用类的方法
Dynamically invoke a class method in Objective C 代码 #import <Foundation/Foundation.h> #import ...
- C#反射实例化类并调用类的方法
反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力. 它允许程序创建和控制任何类的对象,无需提前硬编码目标类: SalBLL a = (SalBLL)Assembly.Load("B ...
- C#如何通过反射调用类下的方法
首先模拟一个mvc的项目,创建一个业务类(HomeService),在类下创建3个方法 public class HomeService { /// <summary> /// 无参方法 ...
- python: 面向对象:类和对象调用类中的变量和方法
一. 面向对象初识 我们在生活中做事都是面向过程的,前面实现一些基本逻辑功能代码也是用面向过程的语句实现的,后来学了函数,把这些功能又装到了函数里.但用面向过程的方法去写程序,只能实现一个功能,我们要 ...
随机推荐
- 为什么不早点使用 Git...
教程:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013739628770 ...
- Python3爬虫(九) 数据存储之关系型数据库MySQL
Infi-chu: http://www.cnblogs.com/Infi-chu/ 关系型数据库关系型数据库是基于关系模型的数据库,而关系模型是通过二维表来保存的,所以关系型数据库的存储方式就是行列 ...
- rhel6.4扩充swap分区
状况:Red hat 6.4 swap分区不足 解决:扩充swap ================================================================== ...
- Educational Codeforces Round 47 (Rated for Div. 2) :B. Minimum Ternary String
题目链接:http://codeforces.com/contest/1009/problem/B 解题心得: 题意就是给你一个只包含012三个字符的字符串,位置并且逻辑相邻的字符可以相互交换位置,就 ...
- Yearning和inception搭建MySQL审核平台
前言 采用开源Yearning和inception开源软件,搭建用于MYSQL审核及线上MYSQL语句更新的审核平台. 功能说明 Yearning: 基于Vue.js与Django的整套mysql-s ...
- 《UML大战需求分析》阅读笔记1
通过阅读本书的序和第一章,让我对于UML的理解更加深刻了,并且懂了怎样把你UML学的更好. 作者先让我们明白什么是UML,大概知道了UML各个图的形态和各种用途,然后再详细的介绍各个图怎样使用. UM ...
- 开启TCP BBR拥塞控制算法
原文来自:https://github.com/iMeiji/shadowsocks_install/wiki/%E5%BC%80%E5%90%AFTCP-BBR%E6%8B%A5%E5%A1%9E% ...
- LeetCode:24. Swap Nodes in Pairs(Medium)
1. 原题链接 https://leetcode.com/problems/swap-nodes-in-pairs/description/ 2. 题目要求 给定一个链表,交换相邻的两个结点.已经交换 ...
- Bug是一种财富-------研发同学的错题集、测试同学的遗漏用例集
此文已由作者王晓明授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 各位看官,可能看到标题的你一定认为这是一篇涉嫌"炒作"的文章,亦或是为了吸引眼球而起的标 ...
- 最后一片蓝海的终极狂欢-写在Win10发布前夕
作为一名Windows8.x+系统平台从业者,从工作伊始,耳边不断充斥着Windows将走向没落的言论,Win10今日晚些时候即将发布,笔者借此机会,说说自己的看法. 早在2012年的时候,IDC曾预 ...