0%

this到底是什麼拉?

不說別的, 每一次都忘記this是什麼, 還要重頭找資源複習這件事很煩, 所以決定提筆來寫, 不是什麼長篇大論, 只求自己了解. (隔句單押)
洗澡的時候想到自己平常在寫sideProject時, 根本就沒有用到這個東西阿…

要分辨this, 就要看他是什麼情況下使用this, 可以分成三種狀況.

  1. 非物件導向下的 this
  2. 單純在物件底下
  3. 物件導向底下的this

非物件導向下的 this

1
2
3
4
5
6
7
8
9
//1
var name = '全域'

function callName() {
var name = '區域'
console.log(this.name);
}

callName()

1
2
3
4
5
6
//2 
var name = '全域';
(function () {
var name = '區域';
console.log(this.name);
})()

來試試看Closure

1
2
3
4
5
6
7
8
// 3
function myEasyCard(callback) {
var money = 100
return callback(money)
}
myEasyCard(function (money) {
console.log(this, money + 100)
})

1
2
3
4
5
6
7
// 4. 
function test() {
console.log(this)
}

test.call('123')
test.call({})

單純在物件底下

1
2
3
4
5
6
7
8
9
10
11
12
13
// 5.
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
//6.
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
//7. 
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
//8.
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
// 9.
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); // this: undefined
test.call('I am call', 1, 2, 3); // this: I am call
test.apply('I am apply', [1, 2, 3]); // this: I am apply
  • 第一個輸出:
    預設值用嚴格模式下是 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()// func: test
obj.inner.test.call(obj.inner) // func: test

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) // 'aaaaa'

透過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()
  1. 在物件導向底下設定this,就是自己這個instance,所以輸出t.
  2. 過100秒後, 執行setTimeout,其實就是一般狀況下的呼叫function,所以會輸出window.
  • 對於箭頭函式來說,事情就不一樣了
1
2
3
4
5
6
7
8
9
10
11
class Test {
run() {
console.log('run this:', this) // 1.
setTimeout(() => {
console.log(this) // 2.
}, 100)
}
}

const t = new Test()
t.run() // 兩者印出一樣, 都是Test

箭頭函式的 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 物件


如果真的什麼都忘記惹, 這張圖希望對你有幫助

點我看答案

  1. 全域
  2. 全域
  3. windows
  4. 1,2,3 / {}
  5. 漂亮阿姨
  6. 漂亮阿姨
  7. 漂亮阿姨, magic watch
  8. 全域