Javascript This

Posted by Noel on 2014-09-21

this 是物件導向語言裡很重要的觀念與應用,像是 Java, PHP 等等。而自己第一次聽到這名詞是大學時學習 Java 的時候,後來接觸 php 時也又碰到了 this,但對 this 到底為何其實都不是很懂,被困擾了很久,單看 this 字面的意思還是讓人覺得抽象,不過當時也有不少同學對於 this 也都是一知半解,但寫程式的時候還是會知道該怎麼使用它,個人覺得這不是個好的現象,所以今天來分享一下對 this 的學習心得,並主要以 javascript 作為範例。

什麼是 this?

this 代表當前的物件,更直白的說就是正在使用的物件為何,我們會用 this 來代表,這是很重要的一點。一般而言會呼叫 this 的場合僅在 method 裡面使用,而 this 代表的對象就是函式所屬的物件

this 簡單使用方式
1
2
3
4
5
6
7
8
9
10
var zilla = {
name: 'godzilla',
size: '100M 60000 tons',
color: 'black',
sayHello: function() {
console.log("Hi, I am "+ this.name); // 這裡的this也可改用zilla來代表,效果是一樣的,因為此時this即是指zilla
}
}

console.log(zilla.sayHello()); // Hi, I am godzilla

this 如何決定?

this的值是由環境 執行期間決定,所以要看呼叫該方法時的環境決定,屬於 window(全域範圍)或某特定物件:

this 的歸屬
1
2
3
4
5
6
7
8
9
10
11
12
var name = "Windows\'s Godzilla";
var myObject = { name: "Object\'s Godzilla" };

var getName = function() {
console.log(this.name);
}
myObject.getName = getName;

myObject.getName();
//Object's Godzilla
getName();
//Windows's Godzilla

當方法裡面包覆方法時,this 會指向全域物件

當物件裡面含有巢狀方法時(方法裡面又包裹方法),則 this 會迷失方向,因此不會參考該物件,轉而參考全域物件,也就是 window 物件

碰到巢狀時,this 會指向全域物件
1
2
3
4
5
6
7
8
9
10
11
12
13
name = "Window Godzilla"
var myObject = {
name: "Object Godzilla",
sayHellow: function() {
console.log("Hello " + this.name)
},
lostThis: function() {
var gg = function (){
console.log(this.name) //在這裡會印出 Window Godzilla 而非 Object Godzilla
}()
}
}
myObject.sayHellow()

即使傳入一個匿名方法,而該方法裡面又有呼叫到 this 時,依然會轉而參考 window 物件

傳入一個帶有呼叫 this 匿名函式給物件方法
1
2
3
4
5
6
7
8
9
10
11
12
name = "Window Object"
var myObject = {
name: "Object Godzilla",
sayHellow: function(callback) {
callback()
console.log("Hello " + this.name)
}
}
myObject.sayHello(function() {console.log("Hellow " + this.name)})
// 印出
// Hellow Window Godzilla
// Hello Object Godzilla

如果怕 this 迷失的話,我們可以明確指定給它

有很多方法可以避免 this 值迷失,常見的有下列幾種

1. 依靠範圍練尋找,把 this 指定給明確變數

1
2
3
4
5
6
7
var myObject = {
name: "Object Godzilla",
sayHello: function() {
var that = this //就在這裡指定
var useThat = function() { console.log(that.name) }(that)
}
}

2. 使用 call 方法來明確指定使用哪個物件當做 this

1
2
3
4
5
6
7
8
name = "Window Name"
var test = function() {
console.log(this.name)
}
test()
test.call({name: "Yoda"})
//如果test方法有定義參數的話,則擺在this參數後方
//test(this, arg1, arg2)

3. 使用 apply 方法來明確指定使用哪個物件當做 this

1
2
3
4
5
6
7
8
9
name = "Window Name"
var test = function(){
console.log(this.name)
}
test()
test.apply({name: "Yoda"})
//使用方式同 call ,差別在於如果有其他參數要使用是全部擺在一個陣列,並在 this 之後的第二個參數傳入
//test(this, [arg1, arg2])


使用 new 來建立函式時,this 會參考到該 new 出來的 instance

聽來有點饒舌,但大概就是這樣的意思,此舉用法上很接近其他 oop 的實體變數的感覺,直接看範例

1
2
3
4
5
6
7
8
9
10
var Godzilla = function() {
this.name = "Godzilla"
this.gender = "Male"
this.color = "Black"
this.getName = function() {
console.log("Hi, my name is " + this.name)
}
}
var jr = new Godzilla()
jr.getName()

若不使用 new 的話,則規則同一般使用方式

在 prototype 裡使用 this ,則會參考到該建構式的 instance

如果說前一則提到的觀念像是使用實體變數的感覺,這邊則像是實體方法裡使用到實體變數的概念,因為定義在 Object.prototype 裡的方法可以被共享,之後該 Object 的實例共享,所以我們可以把它看做是實體方法,裡面呼叫的 this 則自然是參考到該實體的變數囉:

1
2
3
4
5
6
7
8
9
10
11
var Godzilla = function() {
this.name = "Godzilla"
this.gender = "Male"
this.color = "Black"
}
Godzilla.prototype.getName = function() {
console.log("Hi, my name is " + this.name)
}
var jr = new Godzilla()
jr.getName()


結論

其實 this 觀念並沒有這麼難懂,且大多數 oop 的 this 觀念大略是相同的,都是在幫我們釐清我們當前使用或定義的對象為何罷了。只是可能依語言特性而個有些用法上的差異等等,例如 ruby 裡是用 self 幾乎等同 this 的;以 js 為範例介紹,是因 js 上物件的表達方式比較多變,所以 this 的變化也就更多元,較容易搞混,但懂得原理,只要稍微停下來思考相信很快就在也不會被難倒了,或搞混了,

千萬不要迷失自我為何啊

以上若有錯誤或其他建議都歡迎告知或討論唷,感謝^^