This page looks plain and unstyled because you're using a non-standard compliant browser. To see it in its best form, please visit upgrade to a browser that supports web standards. It's free and painless.

Fillano's Learning Notes 會員登入 會員註冊

setTimeout或是setInterval可以接受兩種參數,字串或是參考。如果是字串,他會用eval來處理這個字串,但是eval處理字串時,scope會......在他的execution context中,所以你用字串傳給setTimeout或setInterval,這樣他的scope會在global,接著執行this.shout()就找不到,發生錯誤,因為this這時是window物件,而他沒有叫做shout()的函數可以執行。

如果是參考,他就會執行這個參考,這樣不會有scope問題,例如:

function a() {
	this.shout = function() {
		alert("shouted");
	}
	this.wait1 = function() {
		setTimeout(this.shout,100);
	}
}
var b = new a();
b.wait1();

但是進一步來看,有一個問題,就是在shout方法中會找不到定義在a裡面的東西,因為傳給setTimeout/setInterval執行的參考,他的execution context是在window物件,所以在shout()裡面使用this時,這個this還是指向window物件:

function a() {
	this.shoutStr = "shouted";
	this.shout = function() {
		alert(this.shoutStr);
	}
	this.wait1 = function() {
		setTimeout(this.shout,100);
	}
}
var b = new a();
b.wait1();

這樣alert的結果是undefined。但是如果在global定義一個shoutStr變數,他可以跑出來:

var shoutStr = "window Shout!";
function a() {
	this.shoutStr = "shouted";
	this.shout = function() {
		alert(this.shoutStr);
	}
	this.wait1 = function() {
		setTimeout(this.shout,100);
	}
}
var b = new a();
b.wait1();

因為在瀏覽器這個host環境底下,window物件就是global物件。

但是我還是想取用在a()中間定義的東西,碰到這樣的情況,我通常只好用closure解決:

function a() {
	this.shoutStr = "shouted";
	var ooo = this;
	this.shout = function() {
		alert(ooo.shoutStr);
	}
	this.wait1 = function() {
		setTimeout(this.shout,100);
	}
}
var b = new a();
b.wait1();

詳細的原因,請參考ecma-262 edition3規格。因為是把this.shout當作參數傳給setTimeout,在setTimeout裡面他只是一個變數,把他後面加上()來執行而已。其實可以用一個方法來做實驗:

function a() {
	this.shoutStr = "shouted";
	var ooo = this;
	this.shout = function() {
		alert(ooo.shoutStr);
	}
	this.wait1 = function() {
		c(this.shout);
	}
}
var b = new a();
b.wait1();
function c(d) {
	d();
}

在上面的例子裡,c()這個函數其實跟setTimeout是一樣的,你可以這樣實驗就清楚了。