Javascript 的 this

this

  • this 是 Javascript 的關鍵字
  • this 是 function 執行時自動生成的內部物件
  • 隨著 function 執行場合不同,this 指向的值也會不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var getGender = function(){
return this.gender;
};

var people1 = {
gender: 'female',
getGender: getGender
};

var people2 = {
gender: 'male',
getGender: getGender
};

console.log( people1.getGender() ); // female
console.log( people2.getGender() ); // male

/*
people1 為呼叫 getGender() 的物件,因此 this 指向 people1,而 this.gender 也就等於 people1.gender。

若是直接呼叫 getGender() 則會出現 undefined
*/

this 不等於 function

this 代表的是執行 function 時的物件

1
2
3
4
5
6
7
8
9
10
11
12
var foo = function() {
this.count++;
};

foo.count = 0;

for( var i = 0; i < 5; i++ ) {
foo();
}

// foo.count => 0
// 此時 foo.count 依然等於 0,因為執行 foo() 時的物件為全域,因此 this 則是 window。
1
2
3
4
5
6
7
8
9
10
11
12
var bar = function() {
console.log( this.a );
};

var foo = function() {
var a = 123;
this.bar();
};

foo();

// foo 的執行環境為 window,因此 foo 裡的 this = window,也就是上頭宣告的 bar,而裡頭的 this 也等同 window。因為 window 裡沒有 a 這個屬性,因此會回傳 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
var foo = 'foo'
var obj = {
foo: 'foo in Object'
}

var sayFoo = function () {
console.log(this.foo)
}

obj.sayFoo = sayFoo

obj.sayFoo() // 執行了 obj 裡的 sayFoo,因此會回傳 obj 裡的 foo
sayFoo() // 在 window 下執行 sayFoo 因此會回傳 window.foo => 'foo'

巢狀裡的 this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
func1: function(){
console.log( this === obj );
// 這裡的 this 等同於 obj,因此會回傳 true

var func2 = function(){
// 這裡的 this 跟上層不同!因為它被直接執行,這裡的 this 會變成 window
console.log( this === obj );
};

func2();
}
};

obj.func1();

事件裡的 this

當事件觸發執行某個函式時,觸發的 dom 就是執行環境,this 會指向觸發的那個 dom

1
2
3
4
5
var el = document.getElementById("btn")

el.addEventListener("click", function (event) {
console.log(this) // this => el
}, false);

當程式碼複雜時,例如觸發 ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var $ajax = function(url, callback) {
var request = new XMLHttpRequest();
request.open('GET', url, true);

request.onload = function() {
if (request.status >= 200 && request.status < 400) {
var data = JSON.parse(request.responseText);
if (typeof(callback) === 'function') {
callback.call(null, data);
}
}
};

request.send();
};

// button & click event.
var el = document.getElementById("btn");

// 按下按鈕後執行 ajax,但在 callback function 的 this 卻不是你想像中的那樣
var $ajax = function(url, callback) {
var request = new XMLHttpRequest();
request.open('GET', url, true);

request.onload = function() {
if (request.status >= 200 && request.status < 400) {
var data = JSON.parse(request.responseText);
if (typeof(callback) === 'function') {
callback.call(null, data);
}
}
};

request.send();
};

// button & click event.
var el = document.getElementById("btn");

// 按下按鈕後執行 ajax,但在 callback function 的 this 卻不是你想像中的那樣
el.addEventListener("click", function(event) {
console.log( this.textContent );

$ajax('[URL]', function(res) {
// this.textContent => undefined,因為 $ajax 沒有被指定物件,因此預設為 window
console.log(this.textContent, res);
});
}, false);

為了保留 this 可以先設變數保存下來以便後續程式可隨時調用,避免 this 轉向

1
2
3
4
5
6
7
8
9
10
el.addEventListener("click", function(event) {
// 透過 that 參考
var that = this;
console.log( this.textContent );

$ajax('[URL]', function(res) {
console.log(that.textContent, res);
});

}, false);

或者使用 bind 指定

1
2
3
4
5
6
7
8
9
el.addEventListener("click", function(event) {
console.log( this.textContent );

// 透過 bind(this) 來強制指定該 function scope 的 this
$ajax('[URL]', function(res) {
console.log(this.textContent, res);
}.bind(this));

}, false);

箭頭函式裡的 this

ES6 的箭頭函式有兩個重要特性:更短的函式寫法以及 this 變數強制綁定。這時候的 this 是定義時的物件,而不是執行的環境。依照上方案例可改寫為:

1
2
3
4
5
6
7
8
el.addEventListener("click", function(event) {
console.log( this.textContent );

$ajax('[URL]', (res) => {
console.log(this.textContent, res);
});

}, false);