不說別的, 每一次都忘記this是什麼, 還要重頭找資源複習這件事很煩, 所以決定提筆來寫, 不是什麼長篇大論, 只求自己了解. (隔句單押)
洗澡的時候想到自己平常在寫sideProject時, 根本就沒有用到這個東西阿…
要分辨this, 就要看他是什麼情況下使用this, 可以分成三種狀況.
- 非物件導向下的 this
- 單純在物件底下
- 物件導向底下的this
非物件導向下的 this
1 2 3 4 5 6 7 8 9
| var name = '全域'
function callName() { var name = '區域' console.log(this.name); }
callName()
|
1 2 3 4 5 6
| var name = '全域'; (function () { var name = '區域'; console.log(this.name); })()
|
來試試看Closure
1 2 3 4 5 6 7 8
| function myEasyCard(callback) { var money = 100 return callback(money) } myEasyCard(function (money) { console.log(this, money + 100) })
|
1 2 3 4 5 6 7
| function test() { console.log(this) }
test.call('123') test.call({})
|
單純在物件底下
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 }
auntie.callName();
|
1 2 3 4 5 6 7 8 9 10 11 12
| var name = '全域'; var auntie = { name: '漂亮阿姨', callName: function () { console.log(this.name); } };
(function () { auntie.callName(); })();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function callName() { console.log(this.name); } var auntie = { name: '漂亮阿姨', callName: callName, watch: { name: 'Magic Watch', callName: callName } } auntie.callName(); auntie.watch.callName();
|
1 2 3 4 5 6 7 8 9 10
| var name = '全域'; var auntie = { name: '漂亮阿姨', callName: function() { console.log(this.name); } } var callName = auntie.callName; callName();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var myName = '全域'
function fn(){ console.log(this.myName) }
var obj = { myName: '小明', inner:{ myName: '小明家', fn1: function(){ fn() console.log(this.myName) } } };
obj.inner.fn1()
|
Function的call法補充
call , apply 補充
call, apply 只是讓你call function的時候有別的call法, 而使用這兩種function, 他的第一個參數就是綁定this的值.
1 2 3 4 5 6 7 8 9 10
| 'use strict';
function test(a, b, c) { console.log('this: ', this); console.log(a, b, c); }
test(1, 2, 3); test.call('I am call', 1, 2, 3); test.apply('I am apply', [1, 2, 3]);
|
第一個輸出:
預設值用嚴格模式下是 undefined
第二個輸出:
.call(this, <argument_1, argument_2…>) 因為用call, 所以改變 this 為字串 I am call, a, b, c分別輸出 1, 2, 3
第三個輸出:
.apply(this, [<argument_1, argument_2…>]) 改變 this 為字串 I am apply,第二個參數必須為一個陣列,因此最後會分別輸出1, 2, 3
透過call來進一步了解觀念
1 2 3 4 5 6 7 8 9 10 11
| const obj = { a: 123, inner:{ test:function(){ console.log(this) } } }
obj.inner.test() obj.inner.test.call(obj.inner)
|
call 和 apply相同的地方, 就是第一個參數都是綁定好的this的值. 而相異之處,其實就只是apply可以拿來傳陣列的格式. 或許也可以把它記憶成, 因為資料重要所以要有順序性才能申請(apply), 而且只能申請一次(只有一組資料)
強行綁定 bind
那能不能從此固定 this
的值呢 ?
1 2 3 4 5 6 7 8 9 10 11 12 13
| 'use strict'
const obj = { a: 123, test: function() { console.log(this) } }
const bindTest = obj.test.bind('aaaaa')
bindTest.call(123)
|
透過bind綁定, 他的this永遠都是你當初綁定的值.
arrow function 的this
1 2 3 4 5 6 7 8 9 10 11 12 13
| 'use strict';
class Test { run() { console.log('run this:', this) setTimeout(function(){ console.log(this) }, 100) } }
const t = new Test() t.run()
|
- 在物件導向底下設定this,就是自己這個instance,所以輸出t.
- 過100秒後, 執行setTimeout,其實就是一般狀況下的呼叫function,所以會輸出window.
1 2 3 4 5 6 7 8 9 10 11
| class Test { run() { console.log('run this:', this) setTimeout(() => { console.log(this) }, 100) } }
const t = new Test() t.run()
|
箭頭函式的 this 與在哪裏呼叫有關係,和怎麼呼叫無關,它會與離自己定義的位置最近的 this 一樣。所以 1. 2. 印出一樣
再補充兩個範例
1 2 3 4 5 6 7 8 9 10 11 12
| var name = '全域' var auntie = { name: '阿姨', callSomeone(){ console.log(this.name) setTimeout(() => { console.log(this.name) }, 100); } }
auntie.callSomeone();
|
把第一個callSomeone改成箭頭函式以後….
1 2 3 4 5 6 7 8 9 10 11 12
| var name = '全域' var auntie = { name: '阿姨', callSomeone:()=> { console.log(this.name) setTimeout(() => { console.log(this.name) }, 100); } }
auntie.callSomeone();
|
所以在用箭頭函式的時候要確保他的外層是指向誰, 這樣就知道內層的this指向什麼了.
總結
作用域, closure, Callback function 和this放在哪裡沒有關係, 是要看你怎麼去呼叫他.
一但脫離了物件導向,其實 this 就沒有什麼太大的意義,因為值的改變是要看你如何call它,所以這個值是變動的
非物件導向底下,要看執行環境有不同預設值:
- node.js : global
- 瀏覽器 : window
- 嚴格模式 : undefined ( node.js 跟瀏覽器都一樣 )
物件導向中, this
就是 new 出來的 instance
單純在物件底下,this
跟怎麼呼叫 function 有關
- 如果是在物件本身上呼叫,this 為物件本身
- 把 function 抽出來呼叫,this 則為預設值 ( global or window or undefined )
如何改變 this
:
- call/ apply/ bind : 指定 this,傳入參數並執行 function
- 這三個都是改變this的方法
箭頭函式的this
- 箭頭函式沒有自己的 this
- 跟scope比較像, 會受作用域影響
- 與如何呼叫無關,和你在哪邊呼叫有關
DOM 事件綁定時, this 為綁定的 DOM 物件
如果真的什麼都忘記惹, 這張圖希望對你有幫助
點我看答案
- 全域
- 全域
- windows
- 1,2,3 / {}
- 漂亮阿姨
- 漂亮阿姨
- 漂亮阿姨, magic watch
- 全域