[圖解] 你不知道的 JavaScript – “this”)

來源:互聯網
上載者:User

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 了吧

轉自:http://www.cnblogs.com/ruxpinsp1/archive/2008/04/20/1162463.html 

文中部分內容引自 http://www.quirksmode.org/js/this.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.