[JavaScript 隨筆] 關于 this 的點滴
TL;DR: this 指向調用該方法的對象,只有函數執行時,this 才有定義。
</blockquote>關于 JavaScript 中 this 的坑大家都踩過。像本文開頭的這句話,道理你都懂,但是……所以這里就總結了幾個 this 最常用的使用場景。
全局環境
在瀏覽器的全局環境中,this === window 。
但是,當使用了 ‘use strict'; 進入嚴格模式時,this === undefined 。如果是在 nodejs 環境中,全局對象會更復雜一些,因為它有兩種執行方式。一個是命令行方式,即輸入
shell$ node進入類似于瀏覽器的控制臺一樣的界面,可以逐行執行代碼。那這里的 this 和 global 是嚴格相等的。但如果是
shell$ node program.js這樣執行一個文件的話,nodejs 會為每個文件創建一個自執行匿名函數的塊,這里面的 this 并不是全局對象 global 。但是,如果聲明變量時沒有加 var 的話,這些變量還是會加到 global 上去。
函數調用
函數中的 this 可能更常見一點吧。
javascriptfunction foo() { console.log(this.name); }如果直接判斷這里的 this 是全局對象的話,就太沖動了(還記得最開始這句話嗎?只有當函數調用時才能判斷 this 真正引用的對象是什么)。
如果它作為一個全局函數【foo()】,或者閉包【return foo;】,又或者是回調函數【other(foo)】的話,那么它在執行時就是全局對象了。
還有三個我們經常遇到的方法可以改變 this 的引用,就是 call、apply 和 bind 。
javascriptfoo.call(thisArg);那么這里的 this 就指向了 thisArg,但當它為 null 或者 undefined,this 會指向全局對象。
還有一種函數調用是使用 new 關鍵字。
javascriptnew foo();這里的 this 指向的是構造出來的新對象。
那么其他情況就必然是 x.foo() 類似的調用方式了,看到這條語句的時候再去看 foo 的定義,它里面 this 引用的就是對象 x 。
數學學得好的話,可以反應過來 x 可以表示任何對象,比如 a,a.b,或者其他更復雜的表達式。好,來測試一下,有如下代碼
var o = { foo: function() { return this; } };function bar() { return o.foo; }
console.log(bar()());</pre>
結果是什么?反正就兩個選擇,一是 this 指向全局對象,二是指向 o 。
原型
在上一篇文章 《繼承的實現方式及原型概述》 中其實有提到過一部分原型方面的知識,那這里就稍微深入一點。
這里介紹的是通過 new 創建對象時的 this 。每個函數對象(用 function 關鍵字修飾的變量)自帶 prototype 屬性,在 prototype 上定義的屬性都會繼承給 this 。這些屬性被每個實例共享,實例中會創建所有 prototype 上的屬性,值為這些屬性的引用。
文字一多心里難免煩煩的……
![]()
首先,這里通過 new 創建了兩個實例,它們有相同的 prototype 。原型中的數據是創建在堆上的,所以繼承下來的屬性和方法都會指向同一個引用(左邊的箭頭)。
然后,在 this 上定義的屬性和方法是創建在棧上的,所以這些屬性和方法會有各自獨立的內存區域(右邊的箭頭)。
假如在 this 上定義的變量與 prototype 上沖突了,那么 prototype 中的那個變量會“隱藏”起來。
如果有 child.foo = ‘tom'; 那么思考一下怎樣才能讓 child.foo 重新獲得 prototype 中的 foo 值?
一種是可以直接 Parent.prototype.foo 就完事了。另外一種可以通過
javascriptdelete child.foo; console.log(child.foo); // 重新獲得 prototype 中的 foo 值前者的缺點在于,假設 foo 是個函數的話,那么 Parent.prototype.foo() 時,其中的 this 指向的是 Parent.prototype,而不是 child 實例。
很多道理都很簡單,那么再來考一下……
javascriptvar slice = ___?slice({'0': 'a', '1': 'b', 'length': 2}) => ['a', 'b']</pre>
簡單來說就是用一個表達式定義一個變量(函數),它可以將類數組對象(比如說 arguments)轉化成數組。提示一下,數組對象的 slice 函數本身就是有這個功能的,也就是說 Array.prototype.slice.call(arguments) 可以達到要求。
DOM 事件
聽說長篇大論不會受歡迎,但是我仔細想想還是得把這部分寫下來。
事件有兩種記法,一個是
javascriptel.addEventListener('event', handler);attachEvent 也是類似,那么在 handler 中出現的 this 表示觸發該事件的元素,也就是 el 。
另一種是
javascriptel.onevent = handler;同樣,handler 中的 this 還是 el 。
最后一個題……
javascriptvar o = { foo: function() { return this; } }document.onload = o.foo;</pre>
請問,當 load 事件觸發時,這里的 this 是什么?三個選擇:o, window, document.
小結
從這里開始是“廣告”時間了……
其實 this 的討論可以展開很多,可能上面記的內容中有很多欠缺的地方,這個希望大家可以指正。
那我們的知識庫總是會隨著學習和努力慢慢擴大的,個人能力是一方面,花的精力是另一方面。只要肯靜下心去琢磨,很多“網上各種人說這個難那個難”的知識(比如說閉包、原型,或者和知識面廣度有關的,比如說數組中 slice 的高級用法等),花點時間總會搞懂的。
所以學習沒有捷徑,也沒有培訓班,有心就可以了。
</div> 來自:http://hao.jser.com/archive/8118/