setTimeout的用法详见:http://www.w3school.com.cn/htmldom/met_win_settimeout.asp

是的,setTimeout的常见用法是让某个方法延迟执行。我们知道,setTimeout方法是挂在window对象下的。《JavaScript高级程序设计》第二版中,写到:“超时调用的代码都是在全局作用域中执行的,因此函数中this的值在非严格模式下指向window对象,在严格模式下是undefined”。在这里,我们只讨论非严格模式。

setTimeout接受两个参数,第一个是要执行的代码或函数,第二个是延迟的时间。

一、先说结论:setTimeout中所执行函数中的this,永远指向window!!注意是要延迟执行的函数中的this哦!!

1. 直接使用,代码1.1:

setTimeout("alert(this)", 1);   // [object Window]

2. 在一个对象中调用setTimeout试试,代码1.2:

var obj = {
say: function() {
setTimeout("alert('in obj ' + this)", 0)
}
} obj.say(); // in obj [object Window]

3. 将执行的代码换成匿名函数试试,代码1.3:

var obj = {
say: function() {
setTimeout(function(){alert(this)}, 0)
}
} obj.say(); // [object Window]

4. 换成函数引用再试试吧,代码1.4:

function talk() {
alert(this);
} var obj = {
say: function() {
setTimeout(talk, 0)
}
} obj.say(); // [object Window]

恩,貌似得到的结论是正确的,setTimeout中的延迟执行函数中的this指向了window。这里我反复的强调,是延迟执行函数中的this,是因为,我们经常会面对两个this。一个是setTimeout调用环境中的this,一个就是延迟执行函数中的this。这两个this有时候是不同的。有些不放心??再多写一些代码测试一下!  

二、setTimeout中的两个this到底指向谁??为了便于区分,我们把setTimeout调用环境下的this称之为第一个this,把延迟执行函数中的this称之为第二个this,并在代码注释中标出来,方便您区分。先说得出的结论:第一个this的指向是需要根据上下文来确定的,默认为window;第二个this就是指向window。然后我们通过代码来验证下。

1. 函数作为方法调用还是构造函数调用,this是不同的。先看代码,代码2.1:

function Foo() {
this.value = 42;
this.method = function() {
// this 指向全局对象
alert(this) // 输出window 第二个this
alert(this.value); // 输出:undefined 第二个this
};
setTimeout(this.method, 500); // this指向Foo的实例对象 第一个this
}
new Foo();

我们new了一个Foo对象,那么this.method中的this指向的是new的对象,否则无法调用method方法。但是进了method方法后,方法中的this又指向了window,因此this.value的值为undefined。

我们在外层添加一段代码,再看看,代码2.2:

var value=33;

function Foo() {
this.value = 42;
this.method = function() {
// this 指向全局对象
alert(this) // 输出window 第二个this
alert(this.value); // 输出:33 第二个this
};
setTimeout(this.method, 500); // 这里的this指向Foo的实例对象 第一个this
}
new Foo();

从这里,可以明显的看到,method方法中的this指向的是window,因为可以输出外层的value值。那为什么setTimeout中的this指向的是Foo的实例对象呢?

我觉得代码2.2就等价于下面的代码,如代码2.3:

var value=33;

function Foo() {
this.value = 42;
setTimeout(function(){alert(this);alert(this.value)}, 500); // 先后输出 window 33 这里是第二个this
}
new Foo();

setTimeout中的第一个参数就是一个单纯的函数的引用而已,而函数中的this仍然指向的是window。在setTimeout(this.method, time) 中的this是可以根据上下文而改变的,其最终的目的是要得到一个函数指针。我们再来验证一下,看代码2.4:

function method() {
alert(this.value); // 输出 42 第二个this
} function Foo() {
this.value = 42;
setTimeout(this.method, 500); // 这里this指向window 第一个this
} Foo();

这次我们将Foo当成方法直接执行,method方法放到外层,即挂在window上面。而this则指向了window,因此可以调用method方法。method方法中的this仍然指向window,而Foo()执行的时候,对window.value进行了赋值(this.value=42),因此输出了42。

 

三、实践。知道了得出的结论,我们来阅读一下比较奇葩的一些代码,进行验证。  

首先在一个函数中,调用setTimeout。代码3.1:

var test = "in the window";

setTimeout(function() {alert('outer ' + test)}, 0); // 输出 outer in the window ,默认在window的全局作用域下

function f() {
var test = 'in the f!'; // 局部变量,window作用域不可访问
setTimeout('alert("inner " + test)', 0); // 输出 outer in the window, 虽然在f方法的中调用,但执行代码(字符串形式的代码)默认在window全局作用域下,test也指向全局的test
} f();

在f方法中,setTimeout中的test的值是外层的test,而不是f作用域中的test。再看代码3.2:

var test = "in the window";

setTimeout(function() {alert('outer' + test)}, 0); // outer in the window  ,没有问题,在全局下调用,访问全局中的test

function f() {
var test = 'in the f!';
setTimeout(function(){alert('inner '+ test)}, 0); // inner in the f! 有问题,不是说好了执行函数中的this指向的是window吗?那test也应该对应window下 // 的值才对,怎么test的值却是 f()中的值呢????
} f();

呀。。按照前面的经验,f中的setTimeout中的test也应该明明应该是指向外层的test才对吧???我们注意到,这个f里面的setTimeout中的第一个参数是一个匿名函数,这是上面两端代码最大的不同。而只要是函数就有它的作用域,我们可以将上面的代码替换成下面的代码3.3:

var test = "in the window";

setTimeout(function() {alert('outer ' + test)}, 0); // in the window

function f() {
var test = 'in the f!'; function ff() {alert('inner ' + test)} // 能访问到f中的test局部变量 setTimeout(ff, 0); // inner in the f!
} f();

再看一段更清晰的代码,3.4:

var value=33;

function Foo() {
var value = 42;
setTimeout(function(){alert(value);alert(this.value)}, 500); // 先后输出 42 然后输出33 这里的this是第二个this
}
new Foo();

可以确定,延迟执行函数中的this的确是指向了window,毫无疑问,上面的所有代码都可以验证哈。但是延迟执行函数中的其他变量需要根据上下文来确认。

修改代码3.4为3.5,去掉匿名函数的调用方式,会更加清晰:

var value=33;

function Foo() {
var value = 42;
function ff() {
alert(value); // 42
alert(this.value); // 33
}
setTimeout(ff, 500); // 先后输出 42 33
}
Foo(); // 直接执行,跟普通函数没有区别

因此,如果去掉Foo中的value=42的话,那么value的值等于多少呢?undefined还是外层的33??请看3.5:

var value=33;

function Foo() {
function ff() {
alert(value); // 输出33
alert(this.value); // 输出33 this指向window
}
setTimeout(ff, 500); // 先后输出 33 33
}
Foo();

没错,就是外层的33,因为ff可以访问到window下的value值,就如同setTimeout中的匿名函数一样。    

最后,我们通过对象的方式进行调用,代码3.6:

var obj = {
name: 'hutaoer',
say: function() {
var self = this;
setTimeout(function(){
alert(self); // 输出 object ,指向obj
alert(this); // 第二个this,指向window,我心永恒,从未改变
alert(self.name) // 输出 hutaoer
}, 0)
}
} obj.say();

最后,如果您到看懂了上面的例子,那么我们可以回顾一下得出的一些结论咯:

一、setTimeout中的延迟执行代码中的this永远都指向window

二、setTimeout(this.method, time)这种形式中的this,即上文中提到的第一个this,是根据上下文来判断的,默认为全局作用域,但不一定总是处于全局下,具体问题具体分析。

三、setTimeout(匿名函数, time)这种形式下,匿名函数中的变量也需要根据上下文来判断,具体问题具体分析。### 重新编辑start  谢谢一楼@白夜说 同学的回复,在这里匿名函数的使用形成了一个闭包,从而能访问到外层函数的局部变量。这样子去理解,我觉得挺好的!只是这种闭包,跟常见的闭包不同,因为函数式放在setTimeout里面。 ### —— 于2013.11.29下午15:50分 重新编辑 end

今天就到这里,上面的结论都是本人自己总结出来。鄙人才疏学浅,难免有误,若有纰漏、错误或欠妥之处,还望大家指出,在下不吝赐教,力图互相学习,共同进步。

接下来的一篇,还是谈setTimeout的用法,以及需要注意的问题,敬请期待。

【javascript 技巧】谈谈setTimeout的作用域以及this的指向问题的更多相关文章

  1. Javascript定时器(二)——setTimeout与setInterval

    一.解释说明 1.概述 setTimeout:在指定的延迟时间之后调用一个函数或者执行一个代码片段 setInterval:周期性地调用一个函数(function)或者执行一段代码. 2.语法 set ...

  2. (译文)12个简单(但强大)的JavaScript技巧(二)

    原文链接: 12 Simple (Yet Powerful) JavaScript Tips 其他链接: (译文)12个简单(但强大)的JavaScript技巧(一) 强大的立即调用函数表达式 (什么 ...

  3. JavaScript技巧&写法

    原文:JavaScript技巧&写法 JavaScript技巧篇: 1>状态机 var state = function () { this.count = 0; this.fun = ...

  4. 深入理解setTimeout的作用域

    看了一篇关于setTimeout作用域的问题,其实之前在<javascript高级程序设计>时也看到了,分享给大家: 先总结下: 一.setTimeout中的延迟执行代码中的this永远都 ...

  5. Javascript的那些硬骨头:作用域、回调、闭包、异步……

    终于到了神话破灭的时刻-- 这注定是一篇"自取其辱"的博客,飞哥,你们眼中的大神,Duang,这次脸朝下摔地上了. 故事得从这个求助开始:e.returnValue 报错:未定义, ...

  6. JavaScript:谈谈let和const

    最近接触到ES6的一些相关新特性,想借let和const两个命令谈谈JavaScript在变量方面的改进. 由于let和const有很多相似之处,我们就先说一说let吧. 1. let添加了块级作用域 ...

  7. 【转】45个实用的JavaScript技巧、窍门和最佳实践

    原文:https://colobu.com/2014/09/23/45-Useful-JavaScript-Tips,-Tricks-and-Best-Practices/ 目录 [−] 列表 第一次 ...

  8. JavaScript基础学习-函数及作用域

    函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,作为初学者,我经常有困惑,借助写此博文来巩固下之前学习的内容. (一)JavaS ...

  9. javascript 技巧总结积累(正在积累中)

    1.文本框焦点问题 onBlur:当失去输入焦点后产生该事件 onFocus:当输入获得焦点后,产生该文件 Onchange:当文字值改变时,产生该事件 Onselect:当文字加亮后,产生该文件 & ...

随机推荐

  1. SQL 谜题(父亲的邮票)

    问题:父亲需要些1分,2分,3分,5分,10分的邮票, 其中两种各买四张,另外的三种各买三张 我忘记是哪几种了?他给了我一些10分硬币,金额刚好买这些邮票 计算及分析过程: --通过极限算法,若都是3 ...

  2. 当年只会C# 所以写C++就成这样了! log4cplus -> log4net

    属实C++不会. 目前帮朋友弄个小项目需要小折腾一下. c# 一直采用 log4net ,c++的呢,找找有个log4cplus  知识有限,做个通用类吧.别把精力放在这里. 动手创建个静态类. 为了 ...

  3. OpenCv图像裁剪指点区域_roi

    两种方式Range和ROI #include <opencv2/opencv.hpp> using namespace std; using namespace cv; void test ...

  4. PTAM 编译安装 on MAC(mavericks)

    最近有需要研究PTAM,所以需要在我的MAC上编译安装,整个过程让人非常崩溃各种问题陷阱.现在整理一下编译过程.我已经成功在MAC上编译.那么接下来我会详细讲解整个过程: 注意: 部分链接可能需要FQ ...

  5. LeetCode 344. Reverse String

    Problem: Write a function that takes a string as input and returns the string reversed. Example: Giv ...

  6. Xcode playground markdown常用语法

    //: **Bold** *Italic* /*: # h1 ## h2 ### h3 #### h4 h5 h6 same as h3 --- --- --- * blank seperate li ...

  7. 解决ugui中Image使用iTween的ColorTo、ColorFrom等不生效

    查看iTween的源码找到ColorFrom函数,看该函数的注释“/// Changes a GameObject's color values instantly then returns them ...

  8. dotnetcore 单元测试

    dotnetcore的单元测试目前支持的比较好的是xunit,首先通过nuget添加组件dotnet-test-xunit 和 xunit.如果有依赖注入可在构造方法中,相当于Nunit中的[Setu ...

  9. Redis_redis分布式锁-SETNX

    因业务需要使用了redis的SETNX来实现分布式锁. 描述:Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的缩写,如SETNX命令就应该理解为:SET if Not eXis ...

  10. iOS开发之单元测试

    开始之前 本文侧重讲述如何在iOS程序的开发过程中使用单元测试.使用Xcode自带的OCUnit作为测试框架. 一.单元测试概述 单元测试作为敏捷开发实践的组成之一,其目的是提高软件开发的效率,维持代 ...