❒ 大部分 this 的運作模式
先判斷程式碼是傳統函式或箭頭函式
- 傳統函式中的
this
只與調用方式有關,與怎麼定義 this
無關 。
- 呼叫函式時,前面有任何物件
this
就會指向它,如果 this
前沒有任何物件會指向全域。( 下方會有範例 )
setTimeout
為 callback function 是 simple call 的一種形式,在傳統函式寫法下 this
會指向全域,為了避免 setTimeout
的 this
指向全域可使用「 箭頭函式 」或是「 指向其他變數 」。( 最後範例 8 )
- 箭頭函式沒有自己的
this
,會看外層函式的 this
指向。
- 宣告方式不同 (
var
、const
、let
) ,this
也會不同,要留意。
❒ #1 一個函式中包含多少參數
1 2 3 4 5 6
| var a = '全域' function fn(params) { console.log(params, this, window, arguments); debugger; } fn(1, 2, 3);
|
// 執行 debugger ,開發者工具會跳到 Sources 區域 ▲
console.log(params, this, window, arguments);
→ 中為運行函數本身就可以執行的參數內容
params
為外部傳入的參數。
this
目前是指向 windows
,但實際在運作時可能會有很多種不同的指向,此指向會影響在使用框架時指到哪,如果想要指向特定的元件但卻指向錯誤,就會出錯。
window
瀏覽器本身就存在的全域變數。
arguments
傳統函式會帶入的參數,為類陣列會帶入所有傳入的參數內容。
- 程式中
fn(1, 2, 3);
有三個參數 arguments
會列出所有參數值。
❒ #2 this 的指向為何
1 2 3 4 5 6 7 8
| var obj = { name: '小明', fn: function(params) { console.log(params, this, window, arguments); } } obj.fn(1 , 2 , 3);
|
fn:
的函式程式碼與 #1 範例相同,但卻只有 this
有所不同。
- 1 為外部傳入的參數
{name: "小明", fn: ƒ}
為 this
。
❒ #3 注意:this 的指向相當複雜,大部分情境只需要了解其中一種即可(95%)
傳統函式中的 this
只與調用方式有關,與怎麼定義 this
無關 。
大部分情境只須了解其中一種,請看下方 #4 this 的各種運用變化 | this 的判斷方式
1 2 3 4 5 6 7 8 9
| var someone = '全域'; function callSomeone() { console.log(this.someone); } callSomeone();
|
❒ #4 this 的各種運用變化 | this 的判斷方式
this
的判斷 : ( 此判斷方式以傳統函式為主,箭頭函式的判斷法請看 #5 this
陷阱 )
- 呼叫函式時,前面有任何物件
this
就會指向它 ( 例 : #4 );
- 呼叫函式時,如果前方沒任何物件就會指向全域 ( 例 : #3 的 simple call )。
範例 1
1 2 3 4 5 6 7 8 9 10 11 12 13
| var someone = '全域'; function callSomeone() { console.log(this.someone); } var obj = { someone: '物件', callSomeone() { console.log(this.someone); } } obj.callSomeone();
|
// 呼叫函式時,前面有任何物件 this 就會指向它 ▲
範例 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var someone = '全域'; function callSomeone() { console.log(this.someone); }
var obj2 = { someone : '物件2', callSomeone } obj2.callSOmeone();
|
- 變數 obj2 裡的
callSomeone
指向 function callSomeone()
。
- 呼叫函式時,前面有任何物件
this
就會指向它,所以 this
指向變數 obj2。
範例 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var someone = '全域'; function callSomeone() { console.log(this.someone); } var wrapObj = { someone: '外層物件', callSomeone, innerObj: { someone: '內層物件', callSomeone, } } wrapObj.callSomeone(); wrapObj.innerObj.callSomeone();
|
- 下方程式碼中,外層與內層的物件
callSomeone
皆使用函式 callSomeone()
。
- 呼叫函式時,前面有任何物件
this
就會指向它。
wrapObj.callSomeone();
→ 所以 this
指向 wrapObj
,輸出 外層物件
。
wrapObj.innerObj.callSomeone();
→ 所以 this
指向 innerObj
,輸出 內層物件
。
範例 4
1 2 3 4 5 6 7 8 9 10 11
| var someone = '全域'; function callSomeone() { console.log(this.someone); } var obj3 = { someone: `物件 3`, fn() { callSomeone(); } } obj3.fn();
|
this
與怎麼定義無關,只和調用方式有關,調用這個 fn()
內的 callSomeone()
函式時前方沒有看到任何物件,就屬於 simple call 會指向全域。
obj3.fn();
→ output 全域
- ‼️ 注意 : 通常會於物件下的方式調用 this,不會使用 simple call 方式去取得
this
,結果可能會不如預期,這邊解說範例用。
範例 5
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var someone = '全域'; function callSomeone() { console.log(this.someone); } var obj4 = { someone: '物件 4', fn() { setTimeout(function () { console.log(this.someone); }); } } obj4.fn();
|
範例 6. 請問以下 this 的結果是甚麼 ?
1 2 3 4 5 6 7 8 9
| var obj = { data: { myName: 'hexschool', }, getName: function () { console.log(this.data.myName); } } obj.getName();
|
getName: function ()
為傳統函式,傳統函式的 this 都會看前方呼叫它的是誰。
obj.getName();
結果為 hexschool。
範例 7. 請問以下 this 的結果是甚麼 ?
1 2 3 4 5 6 7 8 9
| var obj = { data: { myName: 'hexschool', }, getName: () => { console.log(this.data.myName); } } obj.getName();
|
- 箭頭函式沒有自己的
this
。它會看外層函式作用域中的 this
指向,如果外層沒有函式則會指向全域。
這邊全域沒東西,所以結果如下。
obj.getName();
結果為 TypeError: Cannot read property 'myName' of undefined
。
範例 8
1 2 3 4 5 6 7 8 9 10 11 12
| var myName = '小明'; var obj = { myName: '小美', x: function () { var myName = '小王'; setTimeout(function () { console.log(this.myName); }, 0); }, y: '2', } obj.x();
|
setTimeout(function (){})
為 callback function ( 大部分屬於 simple call 形式 ),會指向全域。
但要注意,如果 callback function 這邊改為箭頭函式指向就會改變。
obj.getName();
結果為 小明。
參考資訊