JavaScript 的 this 到底是誰?
This 的在 JS 使用上非常頻繁,但一個 this 每個 function 都各自表示,這裡就直接用範例說明 This 有哪些情境,不過請注意一點,影響 this 的是在於函式的呼叫方法 ,並非宣告的時機:
純粹的調用 (Simple call) 如果直接調用函式,此函式的 this 會指向 window,以下兩個範例都是直接調用含式,所以都是指向 window。
1 2 3 4 5 6 7 8 window .auntie = '漂亮阿姨' ;function callAuntie ( ) { console .log('call:' , this .auntie); } callAuntie();
這裡將 function 內在包覆著 function,但只要是直接呼叫,this 都是屬於全域。
1 2 3 4 5 6 7 8 9 10 11 12 window .auntie = '漂亮阿姨' ;function callAuntie ( ) { console .log('call:' , this .auntie); function callAgainAuntie ( ) { console .log('call again:' , this .auntie); } callAgainAuntie(); } callAuntie();
無論在哪一層,純粹的調用方式 this
都會指向 window。
物件的方法調用 (As an object method) 如果 function 是在物件下調用,那麼 this 則會指向此物件,無論 function 是在哪裡宣告。以下的範例中一個是純粹的調用,另一個則是使用物件的方法調用,物件的方法調用時 this
會指向調用的物件。
1 2 3 4 5 6 7 8 9 10 11 12 13 function callName ( ) { console .log(this .name); } var name = '全域阿婆' ;var auntie = { name: '漂亮阿姨' , callName: callName } callName() auntie.callName()
相同的道理,宣告的位置不重要,重要的是呼叫的方法。如果將物件內的函式
賦予在一個純粹的變數上並調用它時,這個 this
將會指向全域。
1 2 3 4 5 6 7 8 9 10 11 var name = '全域阿婆' ;var auntie = { name: '漂亮阿姨' , callName: function ( ) { console .log(this .name); } } callThisName = auntie.callName; callThisName()
DOM 物件調用 (As a DOM event handler) 同此方法 DOM 調用 function 就如同物件調用 function,所以此 this 所指向的則是該 DOM。以下這段程式碼可以貼在任何網頁下的 Console,接下來點擊畫面上任何一區域,該區域則會加上紅線。
1 2 3 4 5 6 7 8 9 var elements = document .getElementsByTagName('div' );function changeDOM ( ) { console .log(this ); this .style.border = '1px solid red' } for (var i = 0 ; i < elements.length; i++) { elements[i].addEventListener('click' , changeDOM, false ); }
建構式的調用 (As a constructor) 在建構式下會 new 一個新物件,此時的 this 會指向新的物件。建構式在後續的章節會介紹,此部分只要了解建構式的 this
也是指像物件本身即可。
1 2 3 4 5 6 function FamilyConstructor ( ) { this .mom = '老媽' } var myFamily = new FamilyConstructor();console .log(myFamily.mom);
這一個 this 不會是全域且可以在生成的物件上重新定義 (所以他指向的是該生成的物件)。
1 2 3 4 5 6 7 8 9 var mom = '全域老媽' ;function FamilyConstructor (newMom ) { this .mom = newMom || '老媽' ; } var myFamily = new FamilyConstructor('希望是漂亮阿姨' );var realFamily = new FamilyConstructor();console .log('我的' , myFamily.mom); console .log('現實' , realFamily.mom);
使用 Call 來呼叫 function call
調用的函式可以直接傳入新的物件,使其作為 this 所指向的物件。
1 2 3 4 5 6 7 var name = '全域阿婆' ;function callName ( ) { console .log(this .name); } callName(); callName.call({name : '漂亮阿姨' });
call, bind, apply 這三者均可,都可以傳入新的 this
給予函式使用,三者僅是使用方法不同,可參考:Function.prototype.apply() - JavaScript | MDN 。
重新指向 this 在實際運作時,立即函式 (IIFE) 或是非同步的事件 (setTimeout) 大多都會指向全域,如果需調用的則是物件本身的話,可以先用一個變數指向 this
,等到調用後再重新使用它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function callName ( ) { console .log('區域' , this .name); var that = this ; setTimeout(function ( ) { console .log('全域' , this .name); console .log('區域' , that.name); }, 10 ); } var name = '全域阿婆' ;var auntie = { name: '漂亮阿姨' , callName: callName } auntie.callName();
這個變數名稱可以自己定義,常見有 that
、vm
、self
等等,可以使用自己或團隊習慣的為主即可。