this
- 每個執行環境都有屬於自己的 this 關鍵字
- this 與函式如何宣告沒有關聯性,僅與呼叫方式有關
- 嚴格模式下,簡易呼叫會有很大個改變
this
的實際指向跟我們如何去呼叫函式是有很大的關聯性的!
this
指的並不是函式本身,而是指向目前呼叫函式或方法的擁有者(owner)物件。所以函式需要了解函式的幾種調用方式,就能一併了解this
在這些情況下的不同。
this: 物件的方式調用(最常見)
- this 與函式如何宣告沒有關聯性,僅與呼叫方式有關
- 物件方法調用時,僅需要關注是在哪一個物件下呼叫
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function callName(e) { console.log(this, this.myName); };
var family = { myName: '小明家', callName: callName, Ming: { myName: '小明', callName: callName } } family.callName(); family.Ming.callName();
|
物件方法調用時,僅需要關注是在哪一個物件下呼叫。
所以family.callName();
的this
就是指向family
;而family.Ming.callName();
的this
就是指向family.Ming
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var myName = '真心鎮大冒險';
function callName(e) { console.log(this, this.myName); };
var family = { myName: '小明家', callName: function(){ console.log(this.myName); } }
var callName = family.callName; callName(); // 真心鎮大冒險
|
this
與函式如何宣告沒有關聯性,僅與呼叫方式有關。
所以我們只要管它是如何被呼叫出來的就好,而 callName()
是在全域裡面被執行出來的,所以它的this
就會指向 window
。
this: 簡易呼叫(simple call)
- 盡可能不要使用 simple call 的
this
簡易呼叫的 this
都會指向 window
簡易呼叫的 this
都會指向 window
簡易呼叫的 this
都會指向 window
1 2 3 4 5 6 7
| var myName = '真心鎮大冒險';
function callName(){ console.log(this.myName); }
callName(); // 真心鎮大冒險
|
原因是因為我們在全域呼叫函式,所以函式的 this
會指向 window
,所以會指到全域變數的 myName。
而這種直接在全域下呼叫的方式就算是「簡易呼叫」的一種。
1 2 3 4 5 6 7 8 9 10
| var myName = '真心鎮大冒險';
// IIFE 立即函式 (function (){ console.log(this.myName); //真心鎮大冒險 function callSomeone(){ console.log(this.myName); } callSomeone(); //真心鎮大冒險 })()
|
this 不會看你的呼叫位置,而是依據呼叫方式而定的。
簡易呼叫(閉包)
1 2 3 4 5 6 7 8 9 10 11 12
| var myName = '真心鎮大冒險';
function easyCard(base) { var money = base; var name = '悠遊卡'; return function (update) { money = money + update; console.log(this.myName, money); } } var MingEasyCard = easyCard(100); MingEasyCard(10); // 真心鎮大冒險 110
|
這邊也可以看到它跟前面的例子一樣都是在全域下直接呼叫,所以它的this
就是指向 window。
簡易呼叫(callback)
所謂的「Callback function」其實就是「把函式當作另一個函式的參數,透過另一個函式來呼叫它」。
以callback function
的方式被呼叫,所以算是「簡易呼叫」。所以 this
會指向 window。
1 2 3 4 5 6 7 8 9
| var myName = '真心鎮大冒險';
function myEasyCard(callback) { var money = 100 return callback(money) } myEasyCard(function (money) { console.log(this.myName, money + 100) });
|
再來下一個範例
1 2 3 4 5 6 7 8 9 10 11
| var myName = '真心鎮大冒險';
var family = { myName: '小明家', callName: function () { setTimeout(function () { console.log(this.myName); }, 1000); } } family.callName(); //真心鎮大冒險
|
this
不會看你是在那裡執行,而是看你是如何去執行它。因為setTimeout
的()內是一個callback function
,所以它是以callback function
的方式被呼叫,所以算是「簡易呼叫」。
如果想要把 this
指向 family
的話,解決方法如下 ↓
1 2 3 4 5 6 7 8 9 10
| var family = { myName: '小明家', callName: function () { var self = this; // vm, that setTimeout(function () { console.log(self.myName); }, 1000); } } family.callName(); //小明家
|
this: call, apply, bind
call
1 2 3 4 5 6 7 8 9
| var family = { name: '小明家' };
var fn = function (para1, para2) { console.log(this, para1, para2); }; fn('小明', '杰倫'); fn.call(family, '小明', '杰倫');
|
call
的語法是能夠把 family
傳入 this
當中,這樣就能指定自己要的this
apply
1 2 3 4 5 6 7 8 9
| var family = { name: '小明家' };
var fn = function (para1, para2) { console.log(this, para1, para2); }; fn.call(family, '小明', '杰倫'); fn.apply(family, ['小明', '杰倫']);
|
apply
的語法其實跟call
滿相近的,但是它後面的參數必須用陣列的方式設定。
bind
bind
的語法跟call
是一樣的方式,但是兩者的不同在於說:「bind
不會被立刻地執行」
1 2 3 4 5 6 7 8 9
| var family = { name: '小明家' };
var fn = function (para1, para2) { console.log(this, para1, para2); }; fn.call(family, '小明', '杰倫'); fn.bind(family, '小明', '杰倫');
|
這裡如果執行的話只會出現 fn.call(family, '小明', '杰倫');
的結果,而bind
的執行結果不會出現。
因為「bind
不會被立刻地執行」
1 2 3 4 5 6 7 8 9 10
| var family = { name: '小明家' };
var fn = function (para1, para2) { console.log(this, para1, para2); }; fn.call(family, '小明', '杰倫'); var fn2 = fn.bind(family, '小明', '杰倫'); fn2();
|
call, apply, bind 進階觀念
以call
, apply
, bind
呼叫函式時,可以綁定this
是什麼。this
會是傳進該函式的第一個參數。
call
, apply
, bind
在非嚴格的模式下,null
, undefined
會被置換成全域變數,而原生型態的值將會被物件包裹呈現
如果把this
傳入純值的話,會用物件包裹的方式呈現。
1 2 3 4 5 6
| var fn = function (para1, para2) { console.log(this, para1, para2); }; fn.call(1, '小明', '杰倫'); fn.call('文字', '小明', '杰倫'); fn.call(true, '小明', '杰倫');
|
嚴格模式(Strict Mode)
1 2 3 4 5
| (function () { 'use strict'; auntie = '漂亮阿姨'; // Uncaught ReferenceError: auntie is not defined })();
|
1 2 3 4 5 6 7
| function callStrict(para1, para2) { 'use strict'; console.log(this, typeof this, para1, para2); } callStrict.call(1, '小明', '杰倫') callStrict.call(undefined, '小明', '杰倫') callStrict('小明', '杰倫')
|
simple call的this
沒有設定的話預設會是undefined
,但是在非嚴格模式的時候會被轉成object
,然而在嚴格模式下它並不會轉化,依舊會維持本來的undefined
,所以盡可能不要使用簡易呼叫的this
!!!
this: DOM
DOM跟this
綁定會讓this
指向觸發事件的元素(HTML element)。
1
| <button onclick="console.dir(this)">按鈕</button>
|
這裡onclick
事件裡的this
,就是指向button
的元素。
1 2 3 4 5 6 7 8 9 10
| var fn = function () { console.dir(this); this.style.backgroundColor = 'orange'; };
var els = document.querySelectorAll('li');
for (let i = 0; i < els.length; i++) { els[i].addEventListener('click', fn, false); };
|
DOM的事件監聽會讓this
指向觸發事件的元素(HTML element)。