问题描述

前些天在练习写一个小例子的时候用到了定时器,发现在setInterval和setTimeout中传入函数时,函数中的this会指向window对象,如下例:

var num = 0;
function Obj (){
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(function(){
console.log(this.num);
}, 1000)
}
}
var obj = new Obj;
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//0  打印的是window.num,值为0

问题原因

从上述例子中可以看到setTimeout中函数内的this是指向了window对象,这是由于setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致这些代码中包含的 this 关键字会指向 window (或全局)对象。详细可参考MDN setTimeout

解决方法

若想要让setTimeout中的this指向正确的值,可以使用以下三种比较常用的方法来使this指向正确的值:

1.将当前对象的this存为一个变量,定时器内的函数利用闭包来访问这个变量,如下:

var num = 0;
function Obj (){
var that = this; //将this存为一个变量,此时的this指向obj
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(function(){
console.log(that.num); //利用闭包访问that,that是一个指向obj的指针
}, 1000)
}
}
var obj = new Obj;
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//1  打印的是obj.num,值为1

这种方法是将当前对象的引用放在一个变量里,定时器内部的函数来访问到这个变量,自然就可以得到当前的对象。

2.利用bind()方法

var num = 0;
function Obj (){
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(function(){
console.log(this.num);
}.bind(this), 1000) //利用bind()将this绑定到这个函数上
}
}
var obj = new Obj;
obj.getNum();//1  打印的为obj.num,值为1
obj.getNumLater()//1  打印的为obj.num,值为1

bind()方法是在Function.prototype上的一个方法,当被绑定函数执行时,bind方法会创建一个新函数,并将第一个参数作为新函数运行时的this。在这个例子中,在调用setTimeout中的函数时,bind方法创建了一个新的函数,并将this传进新的函数,执行的结果也就是正确的了。关于bind方法可参考 MDN bind

3. 箭头函数

var num = 0;
function Obj (){
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(() => {
console.log(this.num);
}, 1000) //箭头函数中的this总是指向外层调用者,也就是Obj
}
}
var obj = new Obj;
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//1  打印的是obj.num,值为1

ES6中的箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj,因此利用箭头函数就可以轻松解决这个问题。

以上三种方法都是比较常用的,当然如果使用call或apply方法来代替bind方法,得到的结果也是正确的,但是call方法会在调用之后立即执行,那样也就没有了延时的效果,定时器也就没有用了,所以推荐使用上述方法来将this传进setTimeout和setInterval中。

最新文章

  1. .Net Office开源组件
  2. 理解 OpenStack + Ceph (4):Ceph 的基础数据结构 [Pool, Image, Snapshot, Clone]
  3. $.ajax()常用方法详解(推荐)
  4. STM32学习笔记(十) CAN通讯测试(环回模式)
  5. Linux安装、卸载软件
  6. Linux中表示“时间”的结构体和相关函数
  7. HDU 1045 - Fire Net (最大独立集)
  8. 搭建SpringMVC+MyBatis开发框架二
  9. Tilera 服务器上hadoop单机版测试
  10. VMware系统运维(二)安装Microsoft .NET 3.5
  11. GDB基本调试
  12. linux驱动系列之调试环境搭建一
  13. Android:DES加密
  14. mysql-cluster集群原理介绍和搭建步骤(四个data/sql节点) (转)
  15. Qt编程之信号与槽-------unresolved external symbol "public: virtual struct QMetaObject const * __thiscall XX::metaObject(void)const
  16. 基于集合成工控机Ubuntu系统安装分区详解
  17. Realm数据持久化方案的简单介绍和使用(二)
  18. DOS学习札记(一)
  19. dotweb框架之旅 [三] - 常用对象-HttpServer
  20. 关于mybatis中传入一个List,字符串数组,或者Map集合作为查询条件的参数

热门文章

  1. linux硬链接和软链接的区别
  2. FAILURE: Build failed with an exception.
  3. Web API 入门系列 - RESTful API 设计指南
  4. Flex知识备忘
  5. WPF中多个RadioButton绑定到一个属性
  6. JS基础回顾,小练习(判断数组,以及函数)
  7. bootstrap精简教程
  8. LeetCode——Serialize and Deserialize Binary Tree
  9. redis主从遇到的两个坑
  10. Java中接口式的匿名内部类的构造方法