setTimeout的用法詳見:http://www.w3school.com.cn/htmldom/met_win_settimeout.asphtml
setTimeout的常見用法是讓某個方法延遲執行。setTimeout方法是掛在window對象下的。《JavaScript高級程序設計》第二版中,寫到:「超時調用的代碼都是在全局做用域中執行的,所以函數中this的值在非嚴格模式下指向window對象,在嚴格模式下是undefined」。在這裏,咱們只討論非嚴格模式。dom
setTimeout接受兩個參數,第一個是要執行的代碼或函數,第二個是延遲的時間。函數
1、先說結論:setTimeout中所執行函數中的this,永遠指向window!!注意是要延遲執行的函數中的this哦!!測試
1. 直接使用,代碼1.1:this
setTimeout("alert(this)", 1); // [object Window]
2. 在一個對象中調用setTimeout試試,代碼1.2:spa
var obj = { say : function () { setTimeout(function () { console.log('setTimeout:'+this); },0); }, hello : function () { console.log('hello:'+this); } }; obj.say(); //setTimeout:[object Window] obj.hello(); //hello:[object Object]
3. 換成函數引用再試試吧,代碼1.3:設計
var hello = function () { alert( this ); } var obj = { say : function () { setTimeout( hello ,1 ); } }; obj.say(); //[object Window]
恩,貌似獲得的結論是正確的,setTimeout中的延遲執行函數中的this指向了window。這裏我反覆的強調,是延遲執行函數中的this,是由於,咱們常常會面對兩個this。一個是setTimeout調用環境中的this,一個就是延遲執行函數中的this。這兩個this有時候是不一樣的。有些不放心??再多寫一些代碼測試一下!指針
2、setTimeout中的兩個this到底指向誰??爲了便於區分,咱們把setTimeout調用環境下的this稱之爲第一個this,把延遲執行函數中的this稱之爲第二個this,並在代碼註釋中標出來,方便區分。先說得出的結論:第一個this的指向是須要根據上下文來肯定的,默認爲window;第二個this就是指向window。而後咱們經過代碼來驗證下。code
1. 函數做爲方法調用仍是構造函數調用,this是不一樣的。先看代碼,代碼2.1:htm
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。
3、實踐。知道了得出的結論,咱們來閱讀一下比較奇葩的一些代碼,進行驗證。
首先在一個函數中,調用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();
最後,若是您到看懂了上面的例子,那麼咱們能夠回顧一下得出的一些結論咯:
1、setTimeout中的延遲執行代碼中的this永遠都指向window
2、setTimeout(this.method, time)這種形式中的this,即上文中提到的第一個this,是根據上下文來判斷的,默認爲全局做用域,但不必定老是處於全局下,具體問題具體分析。
3、setTimeout(匿名函數, time)這種形式下,匿名函數中的變量也須要根據上下文來判斷,具體問題具體分析。