讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議88:this是動態指針,不是靜態引用 >

建議88:this是動態指針,不是靜態引用

this是一個動態指針。在JavaScript中,類似指針特性的標識符還有以下3個。

❑callee:函數的參數集合包含的一個靜態指針,它始終指向參數集合所屬的函數。

❑prototype:函數包含的一個半靜態指針,在默認狀態下它始終指向函數附帶的原型對象,不過可以改變這個指針,使它指向其他對象。

❑constructor:對像包含的一個指針,它始終指向創建該對象的構造函數。

this所指向的對象是由this所在的執行域決定的,而不是由this所在的定義域決定(如圖4.6所示)。this是JavaScript執行作用域的一個屬性,它的指針始終指向當前調用對象。

圖 4.6 this指針的變化示意圖

JavaScript中的函數可以在多個地方被引用,而且這種引用是在執行時才確定的。因此,JavaScript方法的靈活性注定了this指針只能在運行環境中動態地確定。因此,只有當this被最後執行時才能夠準確確定它所指代的對象。

在JavaScript中,閉包扮演著非常重要的角色,它能夠改變this的指向。例如,在下面這個示例中,在一個對像中(如o1)引用或調用另一個對像(如o)的方法時,該方法中的this指針是變化的。當引用對像o的方法f時,它的this就會根據執行對像而定,但在調用對像o的方法f時,它的this還是指向原定義的對象o。


var name=\"this=window\";

var o={

name:\"this=o\",

f:function{

return this;

}

};

var o1={

name:\"this=o1\",

f:o.f;//引用對像o中的方法f

}

var a=o1.f;

alert(a.name);//this實際指向對像為o1


下面嘗試把對像o的方法f封裝在閉包中,然後再進行引用:


var o1={

name:\"this=o1\",

f:function{//this使用閉包封裝方法的引用

return o.f;

}

}

var a=o1.f;

alert(a.name);//this實際指向對像為Window


方法f中的this既不指向對像o,也不指向對像o1,而是指向對像Window。因為在執行對像o1的方法f時,僅指定了閉包運行的上下文環境,即執行閉包的對象為o1,但是閉包內「包裹」的方法f並沒有被執行,當再次執行閉包內的方法時,執行環境已經發生了變化,執行對像從o1變為全局對像Window,所以this就指向了Window。

call和apply方法能夠強制改變函數的執行作用域,它們會破壞函數引用和調用的一般規律,因此使用這兩個方法可以強制地指定this的指代對象。異步調用也會破壞this指針應用的一般規律,這是因為函數在被傳遞給定時器或事件處理函數時才被調用,這破壞了函數的上下文運行環境。下面結合代碼具體進行說明:

(1)指向當前DOM對像

下面這個按鈕定義了一個單擊事件屬性,其中包含了this關鍵字。


<input type=\"button\"onclick=\"this.value=\'是我呀,我就是主人\'\"/>


其中,onclick事件屬性中包含的this就代表當前DOM對像input。

(2)指向構造函數的實例對像

定義一個構造函數,在其中使用this關鍵字作為臨時代表,然後使用new運算符實例化構造函數。


function F{

this.name=\"我就是主人\";

}

var f=new F;

alert(f.name);


這裡的this就代表當前實例對像f。

(3)指向當前對像直接量

下面是一個對像直接量,它包含了兩個屬性,其中方法me返回關鍵字this。


var o={

name:\"我是對像o\",

me:function{

return this;

}

}

var who=o.me;

alert(who.name);//讀取this所代表對象的屬性name,返回字符串\"我是對像o\"


在調用對像直接量o的方法me後,變量who的值就是this的值,它代表當前對像直接量o,然後讀取對像o的屬性name,返回字符串\"我是對像o\"。

(4)指向全局對像

在函數f中調用this關鍵字,並且為this定義並初始化一個屬性name,在調用函數之後,可以直接讀取屬性name。


function f{

this.name=\"我是誰?\";

}

f;//調用方法f

alert(name);//直接讀取屬性name,返回值為\"我是誰?\"


在函數f中,this實際上就是代表window。實際上,上面代碼可以寫成如下形式:


window.f=function{

this.name=\"我是誰?\"

}

window.f;

alert(window.name);


(5)指向當前作用域對像

this關鍵字並不總是代表當前對象,下面這個示例能夠很好地說明這個問題。


<input type=\"button\"onclick=

\"this.value=\'是我呀,我就是主人\'\"/>

<input type=\"button\"onclick=\"f\"/>

<script language=\"javascript\"type=\"text/javascript\">

function f{

this.;

}

</script>


上面示例中的第一個按鈕的事件屬性中包含的this就指向當前對象。但由於在第二個按鈕的鼠標單擊事件屬性中以調用的方式調用全局作用域中的函數f,所以其中的this就代表Window對象,而不是當前按鈕(第二個按鈕)。

要是改變函數的用法,先在腳本中獲取第二個按鈕對象的引用,然後把函數f作為一個值傳遞給對象的onclick事件屬性,代碼如下:


<input type=\"button\"/>

<script language=\"javascript\"type=\"text/javascript\">

var btn2=document.getElementsByTagName(\"input\")[1];

btn2.onclick=f;

function f{

this.;

}

</script>


在上面的示例中,因為this關鍵字就表示按鈕對像本身,所以單擊按鈕之後就能夠改變按鈕的值。這也說明在將函數賦值給按鈕對像之後,雖然函數的定義作用域沒有發生變化,但它的執行作用域已經從全局作用域變為對像作用域,因此,this關鍵字所代表的對象也會隨之發生變化。

如果以同樣的方式把該函數作為事件處理函數賦值給不同的按鈕對象,那麼this會分別代表不同的按鈕對象。這也說明如果改變函數的執行作用域,那麼函數所包含的this關鍵字也會指向不同的對象。

下面這個示例可以更好地說明作用域對於this關鍵字的影響。


function f{

return this;

}

var o={

name:\"對像o\",

me:f,

o1:{

name:\"對像o1\",

me:f,

o2:{

name:\"對像o2\",

me:f

}

}

}

var who=o.o1.o2.me;

alert(who.name);//字符串\"對像o2\",說明當前this代表對像o2

var who=o.o1.me;

alert(who.name);//字符串\"對像o1\",說明當前this代表對像o1

var who=o.me;

alert(who.name);//字符串\"對像o\",說明當前this代表對像o


首先,定義函數f,設置其返回值為this關鍵字,然後把該函數作為值傳遞給不同作用域中的屬性me,最後分別調用它們,這時會發現this所代表的對象是不同的。

但是,如果不是以值的方式傳遞函數f,而是直接調用,則會發現其包含的this都代表Window對象。也就是說,this的執行作用域並沒有發生變化,仍然根據定義它的作用域來確定,即全局作用域。