JavaScript -"this"

weixin_30639719發表於2020-04-05

你不知道的JavaScript -"this"

作者:棕熊[http://www.cnblogs.com/ruxpinsp1]

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

  1. <input type="submit" value="提交" 
    onclick="this.value='正在提交資料'" /> 

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

大家看看這個例子:

  1. var foo = function() {   
  2.     console.log(this);  
  3. }  
  4. foo();  
  5. new foo(); 

比較一下foo() 和 new foo() 的執行結果,你會發現前者this指向的並非foo本身,而是當前頁面的window物件,而後者才真正地指向foo。這是為什麼呢?

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

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

在前面的例子裡,foo() 函式屬於全域性作用域(這裡就是window物件),所以this的值是當前的window物件。而new foo()這樣的形式,其實是建立了一個foo()的副本,並在這個副本上進行操作,所以這裡的this就是foo()的這個副本。

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

  1. <input type="button" id="aButton" value="demo" onclick="" /> 
  2. <script type="text/JavaScript"> 
  3. function demo() {    this.value = Math.random();  
  4. }  
  5. < SPAN>script> 

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

 
如果我們通過建立副本的方式,將這個函式的副本新增到一個HTML元素中,那麼它的所有者就成了這個元素,this也指代了這個元素:
  1. document.getElementById("aButton").onclick = demo

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

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


但是如果這樣定義某個元素的onlick事件:
  1. <input type="button" id="aButton" value="demo" onclick="demo()" /> 

單擊這個button之後,你會發現程式又會報錯了-this又指向了window!

其實,這種方法並沒有為程式建立一個函式,而只是引用了這個函式。

具體看一下區別吧,使用建立函式副本的方法:

  1. <input type="button" id="aButton" value="demo" /> 
  2. <script type="text/JavaScript"> 
  3. var button = document.getElementById("aButton");  
  4. function demo() {  
  5.     this.value = Math.random();  
  6. }  
  7. button.onclickdemo;  
  8. alert(button.onclick);  
  9. < SPAN>script> 

得到的輸出是:

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

使用函式引用的方法:

  1. <input type="button" id="aButton" value="demo" 
    onclick="demo()" /> 
  2. <script type="text/JavaScript"> 
  3. var button = document.getElementById("aButton");  
  4. function demo() {  
  5.     this.value = Math.random();  
  6. }  
  7. alert(button.onclick);  
  8. < SPAN>script> 

得到的輸出是:

  1. function onclick() {  
  2.     demo();  

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

 

這樣就又引出了一個問題:既然函式副本這麼好用,為什麼還需要函式引用的方法呢?答案是效能。每新建一個函式的副本,程式就會為這個函式副本分配一定的記憶體。而實際應用中,大多數函式並不一定會被呼叫,於是這部分記憶體就被白白浪費了。而使用函式引用的方式,程式就只會給函式的本體分配記憶體,而引用只分配指標,這樣效率就高很多。

程式設計師麼,節約為主,所以我們來看一個更好的解決方案:

  1. <script type="text/JavaScript"> 
  2. function demo(obj)  {  
  3.     obj.value = Math.random();  
  4. }  
  5. < SPAN>script> 
  6. <input type="button" value="demo" onclick="demo(this)" /> 
  7. <input type="button" value="demo" onclick="demo(this)" /> 
  8. <input type="button" value="demo" onclick="demo(this)" /> 

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

最後再多講一句:在前面的文章裡,我特別強調了"如果沒有指定this的話"。其實this是可以指定的。Function物件有兩個方法:call()和apply()。這兩個方法都支援指定函式中的this。可以去查一下JavaScript的手冊,看看這兩個函式是幹什麼用的。我們經常用的new foo() 可以用以下這段虛擬碼來描述:

  1. function new (somefunction)  {  
  2. var args = [].slice.call(arguments, 1);  
  3.     somefunctionsomefunction.prototype.
    constructor
     = somefunction;  
  4.     somefunction.apply(somefunction.prototype, args);  
  5.     return somefunction.prototype;  

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

轉載於:https://www.cnblogs.com/judypol/archive/2009/08/16/1547149.html

相關文章