1. 程式人生 > >閉包和this

閉包和this

style log turn 活動對象 明顯 alert ont 包含 裏的

在閉包中使用this對象也可能會導致一些問題。我們知道,this對象是在運行時基於函數的執行環境綁定的:在全局函數中,this等於window,而當函數被作為某個對象的方法調用時,this等於那個對象。不過,匿名函數的執行環境具有全局性,因此其this對象通常指向window。但有時候由於編寫閉包的方式不同,這一點可能不會那麽明顯。下面來看一個例子。

var name = "The Window";
var object = {
    name:"My Object",
    getNameFunc:function(){
        return function(){
            
return this.name } } } alert(object.getNameFunc()()); // "The Window" (在費嚴格模式下)

以上代碼先創建了一個全局變量name,又創建了一個包含name屬性的對象。這個對象還包含一個方法——getNameFunc(),他返回一個匿名函數,而匿名函數又返回this.name。由於getNameFunc()返回一個函數,因此調用object.getNameFunc()()就會立即調用它返回的函數,結果就會返回一個字符串。然而,這個例子返回的字符串是"The Window",即全局name變量的值。為什麽匿名函數沒有取得其包含作用域(或外部作用域)的this對象呢?

前面曾經提到過,每個函數在被調用時,其活動對象都會自動取得兩個特殊變量:this和arguments。內部函數在搜索這兩個變量時,只會搜索到其活動對象為止,因此永遠不可能直接訪問外部函數中的這兩個變量(這一點通過圖7-2可以看得更清楚)。不過,把外部作用域中的this對象保存在一個閉包能夠訪問到的變量裏,就可以讓閉包訪問該對象了,如下所示。

var name = "The Window";
var object = {
    name:"My Object",
    getNameFunc:function(){
        var that =this;
        return
function(){ return that.name; } } } alert(object.getNameFunc()()); // "My Object"

代碼中突出的行展示了這個例子和前一個例子之間的不同之處。在定義匿名函數之前,我們把this對象賦值給了一個名叫that的變量。而在定義了閉包之後,閉包也可以訪問這個變量,因為他是我們在包含函數中特意生命的一個變量。計時在函數返回之後,that也仍然引用著object,所以調用object.getNameFunc()()就返回了"My Object。

在幾種特殊的情況下,this的值可能會意外的改變。比如,下面的代碼就是修改前面例子的結果。

var name = "The Window";
var object = {
    name:"My Object",
    getName:function(){
        return this.name;
    }
}

這裏的getName()方法只簡單地返回this.name的值。一下是幾種調用object.getName()的方式以及各自的結果。

object.getName(); // "My Object"

(object.getName)(); // "My Object"

(object.getName=object.getName)(); // "The Window" 在非嚴格模式下

第一行代碼跟平常一樣調用了object.getName(),返回的是“My Object”,因為this.name就是object.name。第二行代碼在調用這個方法前先給它加上了括號,雖然加上括號之後,就好像只是在引用一個函數,但是this的值得到了維持,因為object.getName和(object.getName)的定義是相同的。第三行代碼先執行了一條賦值語句,然後再調用賦值後的結果。因為這個賦值表達式的值是函數本身,所以this的值不能得到維持,結果就返回了"The Window"。

當然,不大可能會像第二行和第三行代碼一樣調用這個方法。不過,這個例子有助於說明即使語法的細微變化,都有可能意外改變this的值。

閉包和this