你不知道的 JavaScript - “this”

作者: 棕熊  來源: cnblogs  發布時間: 2008-08-16 08:59  閱讀: 3818 次  推薦: 5   原文鏈接   [收藏]  

JavaScript 里的 this 到底指得是什么?很多人都會告訴你 this 指的是當前對象。這樣理解對么?在大多數情況下確實沒錯。比如我們經常會在網頁上寫這樣的 JavaScript:

 

<input type="submit" value="提交" onclick="this.value='正在提交數據'" />

 

這里的this顯然指的是當前對象,即這個提交按鈕。通常,我們使用this的情況都與此類似。但是有什么情況不是這樣的呢?

大家看看這個例子:

 

var foo = function() {
    console.log(
this);
}

foo();
new foo();

 

比較一下 foo() 和 new foo() 的運行結果,你會發現,前者 this 指向的并非 foo 本身,而是當前頁面的window對象,而后者才真正的指向foo。這是為什么呢?

其實這牽涉到JavaScript的一條重要特性,就是所謂的“閉包”。閉包這個概念說復雜也不復雜,但也不是簡單到能用一兩句話說清。偶會在以后的文章中深入探討這個Javascript 最重要的特性。現在,我要告訴大家的是,因為閉包的存在,JavaScript中的作用域變得相當重要。

所謂的作用域,簡單的說,就是創建一個函數時在什么環境下創建的。而this變量的值,如果沒有指定的話,就是函數當前的作用域。

 

在前面的例子里,foo() 函數是在全局作用域(這里就是window對象),所以this的值是當前的window對象。而通過 new foo() 這樣的形式,其實是創建了一個foo()的副本,并在這個副本上進行的操作,所以這里的this就是foo()的這個副本。

 

這樣講可能有點抽象,大家來看個實際的例子:

 

<input type="button" id="aButton" value="demo" onclick="" />
<script type="text/javascript">
function demo() {
    
this.value = Math.random();
}

</script>

 

如果直接調用demo() 函數,程序就會報錯,因為demo函數是在window對象中定義的,所以demo的擁有者(作用域)是window,demo的this也是window。而window是沒有value屬性的,所以就報錯了。

圖解

如果我們通過創建副本的方式,將這個函數的副本添加到一個HTML元素,那么他的所有者就成了這個元素,this也指代了這個元素:

 

document.getElementById("aButton").onclick = demo;

 

這樣就將aButton的onlick屬性設置為demo()的一個副本,this也指向了aButton。

圖解

你甚至可以為多個不同的HTML元素創建不同的函數副本。每個副本的擁有者都是相對應的HTML元素,各自的this也都指向他們的擁有者,不會造成混亂。

圖解

 

但是,如果你這樣定義某個元素的onlick事件:

 

<input type="button" id="aButton" value="demo" onclick="demo()" />

 

點擊這個button之后,你會發現,程序又會報錯了——this又指向了window!

其實,這種方法并沒有為程序創建一個函數,而只是引用了這個函數。

具體看一下區別吧。

 

使用創建函數副本的方法:

 

<input type="button" id="aButton" value="demo" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
    
this.value = Math.random();
}

button.onclick= demo;
alert(button.onclick);
</script>

 

得到的輸出是:

 

function demo() {
    
this.value = Math.random();
}

 

使用函數引用的方法:

 

<input type="button" id="aButton" value="demo" onclick="demo()" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
    
this.value = Math.random();
}

alert(button.onclick);
</script>

 

得到的輸出是:

 

function onclick() {
    demo();
}

 

這樣就能看出區別了吧。函數引用的方式中,onclick事件只是直接調用demo()函數,而demo()函數的作用域仍舊是window對象,所以this仍然指向window。

圖解

 

這樣就又引出了一個問題:既然函數副本這么好用,為什么還需要函數引用的方法呢?答案是性能。每新建一個函數的副本,程序就會為這個函數副本分配一 定的內存。而實際應用中,大多數函數并不一定會被調用,于是這部分內存就被白白浪費了。而使用函數引用的方式,程序就只會給函數的本體分配內存,而引用只 分配指針,這樣效率就高很多。程序員么,節約為主,恩

所以我們來看一個更好的解決方案:

 

<script type="text/javascript">
function demo(obj) {
    obj.value 
= Math.random();
}

</script>
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />

 

這樣,效率和需求就都能兼顧了。

 

最后再多講一句:在前面的文章里,我特別強調了“如果沒有指定this的話”。其實this是可以指定的。Function對象有兩個方法:call()和apply()。這兩個方法都支持指定一個函數中的this。有興趣的話您可以去查一下Javascript的手冊,看看這兩個函數都是干什么用的。而我們經常用的 new foo() 可以用以下這段偽代碼來描述:

 

function new (somefunction) {
    
var args = [].slice.call(arguments, 1);
    somefunction.prototype.constructor 
= somefunction;
    somefunction.apply(somefunction.prototype, args);
    
return somefunction.prototype;
}

 

現在明白了,在本文開頭的第一個例子里,new foo() 的 this 為什么是 foo 了吧

5
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()