Javascript中this關鍵字詳解


Quiz

請看下面的代碼,最后alert出來的是什么呢?

 1 var name = "Bob";  
 2 var nameObj ={  
 3     name : "Tom",  
 4     showName : function(){  
 5         alert(this.name);  
 6     },  
 7     waitShowName : function(){  
 8         setTimeout(this.showName, 1000);  
 9     }  
10 };  
11 
12 nameObj.waitShowName();

要解決這個問題我們需要了解Javascript的this關鍵字的用法。

 

this指向哪里?

一般而言,在Javascript中,this指向函數執行時的當前對象。

In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used within methods to refer to the object on which a method is being invoked.

——jQuery Fundamentals (Chapter 2), by Rebecca Murphey

值得注意,該關鍵字在Javascript中和執行環境,而非聲明環境有關。

The this keyword is relative to the execution context, not the declaration context.

我們舉個例子來說明這個問題:

var someone = {
    name: "Bob",
    showName: function(){
        alert(this.name);
    }
};

var other = {
    name: "Tom",
    showName: someone.showName
}

other.showName();  //Tom

this關鍵字雖然是在someone.showName中聲明的,但運行的時候是other.showName,所以this指向other.showName函數的當前對象,即other,故最后alert出來的是other.name。

 

沒有明確的當前對象時

當沒有明確的執行時的當前對象時,this指向全局對象window。

By default, this refers to the global object.

為什么說是全局對象(the global object),因為非瀏覽器情況下(例如:nodejs)中全局變量並非window對象,而就是叫“全局變量”(the global object)。不過由於我們這片文章主要討論的是前端開發知識,所以nodejs就被我們忽略了。

例如對於全局變量引用的函數上我們有:

var name = "Tom";

var Bob = {
    name: "Bob",
    show: function(){
        alert(this.name);
    }
}

var show = Bob.show;
show();  //Tom

你可能也能理解成show是window對象下的方法,所以執行時的當前對象時window。但局部變量引用的函數上,卻無法這么解釋:

var name = "window";

var Bob = {
    name: "Bob",
    showName: function(){
        alert(this.name);
    }
};

var Tom = {
    name: "Tom",
    showName: function(){
        var fun = Bob.showName;
        fun();
    }
};

Tom.showName();  //window

 

setTimeout、setInterval和匿名函數

文章開頭的問題的答案是Bob。

在瀏覽器中setTimeout、setInterval和匿名函數執行時的當前對象是全局對象window,這條我們可以看成是上一條的一個特殊情況。

所以在運行this.showName的時候,this指向了window,所以最后顯示了window.name。

瀏覽器中全局變量可以當成是window對象下的變量,例如全局變量a,可以用window.a來引用。

我們將代碼改成匿名函數可能更好理解一些:

var name = "Bob";  
 var nameObj ={  
     name : "Tom",  
     showName : function(){  
         alert(this.name);  
     },  
     waitShowName : function(){  
         !function(__callback){
            __callback();
        }(this.showName);  
     }  
 };  
 
 nameObj.waitShowName();  //Bob

在調用nameObj.waitShowName時候,我們運行了一個匿名函數,將nameObj.showName作為回調函數傳進這個匿名函數,然后匿名函數運行時,運行這個回調函數。由於匿名函數的當前對象是window,所以當在該匿名函數中運行回調函數時,回調函數的this指向了window,所以alert出來window.name。

由此看來setTimeout可以看做是一個延遲執行的:

function(__callback){
    __callback();
}

setInterval也如此類比。

但如果我們的確想得到的回答是Tom呢?通過一些技巧,我們能夠得到想要的答案:

var name = "Bob";  
var nameObj ={  
    name : "Tom",  
    showName : function(){  
        alert(this.name);  
    },  
    waitShowName : function(){
        var that = this;
        setTimeout(function(){
            that.showName();
        }, 1000);
    }
}; 
 
 nameObj.waitShowName();  //Tom

在執行nameObj.waitShowName函數時,我們先對其this賦給變量that(這是為了避免setTimeout中的匿名函數運行時,匿名函數中的this指向window),然后延遲運行匿名函數,執行that.showName,即nameObj.showName,所以alert出正確結果Tom。

 

eval

對於eval函數,其執行時候似乎沒有指定當前對象,但實際上其this並非指向window,因為該函數執行時的作用域是當前作用域,即等同於在該行將里面的代碼填進去。下面的例子說明了這個問題:

var name = "window";

var Bob = {
    name: "Bob",
    showName: function(){
        eval("alert(this.name)");
    }
};

Bob.showName();    //Bob

 

apply和call

apply和call能夠強制改變函數執行時的當前對象,讓this指向其他對象。因為apply和call較為類似,所以我們以apply為例:

var name = "window";
    
var someone = {
    name: "Bob",
    showName: function(){
        alert(this.name);
    }
};

var other = {
    name: "Tom"
};    

someone.showName.apply();    //window
someone.showName.apply(other);    //Tom

apply用於改變函數執行時的當前對象,當無參數時,當前對象為window,有參數時當前對象為該參數。於是這個例子Bob成功偷走了Tom的名字。

 

new關鍵字

new關鍵字后的構造函數中的this指向用該構造函數構造出來的新對象:

function Person(__name){
    this.name = __name;        //這個this指向用該構造函數構造的新對象,這個例子是Bob對象
}
Person.prototype.show = function(){
    alert(this.name);
}

var Bob = new Person("Bob");
Bob.show();        //Bob

 

思考題

1.  請問下面代碼會alert出什么,為什么?

var name = "Bob";  
var nameObj ={  
    name : "Tom",  
    showName : function(){  
        alert(this.name);  
    },  
    waitShowName : function(){
        var that = this;
        setTimeout("that.showName();", 1000);
    }
}; 
 
nameObj.waitShowName();

2.  請問下面代碼會alert出什么,為什么?

var fun = new Function("alert(this)");
fun();

3.  下面代碼分別在IE和其他瀏覽器上運行有什么差異,可以用什么方法解決這個差異問題?

IE:

<button id = "box" name = "box">Click Me!</button>

<script>
    var name = "window";

    function showName(){
        alert(this.name);
    }

    document.getElementById("box").attachEvent("onclick", showName);
</script>

Others:

<button id = "box" name = "box">Click Me!</button>

<script>
    var name = "window";

    function showName(){
        alert(this.name);
    }

    document.getElementById("box").addEventListener("click", showName, false);
</script>

 

參考文獻

Javascript Closures . Richard Cornford  . March 2004

Javascript的this用法 . 阮一峰 . 2010.4.30


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM