iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 8
0

this是什麼,取決於被呼叫的呼叫地點。

昨天有提到說,呼叫函式時候會傳遞隱含參數: arguments 和 this 並講解了 arguments ,今天我們就來探討 this 吧!

什麼是 this

我們都會呼叫函式來使用,但有想過到底是從哪裡呼叫到這個函式嗎?
this 通常被稱作函式背景空間( function context),也就是說透過 this 我們可以知道到底是由誰呼叫這支函式。我們無法在一開始定義它,只有函式呼叫時候才能確定。

函式的呼叫有四種方式:

  1. 作為函式呼叫
  2. 作為一個(物件)的方法呼叫
  3. 作為一個建構式函式呼叫
  4. 透過 applycall呼叫

applycall我們會在下一篇中介紹,我們今天主要介紹上述三種呼叫方式。

作為函式呼叫

function funA(){
    console.log(this);
}
funA();//Window 

由於是在全域環境呼叫 funA ,所以這時候的 this 就會是 window

所以很多剛學習的人都會遇到這個問題:

function funA(){
    this.count ++;
}
funA.count = 0;
funA();
console.log(funA.count);//0

明明已經宣告 funA.count = 0 怎麼會沒有加到呢?這就是因為 funA 是被全域環境也就是 window 呼叫,所以 this 並不是你預期的 funA

那到底加了誰的 count 呢? 你可以嘗試印出 window.count 會發現是 NaN ,是因為你賦予 window 一個 count 屬性且一開始沒有給定初始值,
所以會變成 undefined+ 1 = NaN

或許你認為這樣就可以解決問題:

function funA(val){
    val.count ++;
}
var dart = {
    count:0
};
funA(dart);
console.log(dart.count);//1

但這樣你只是在逃避 this 這個問題而已!!
要透過 this 解決方案有兩種,一個是等下提到的作為物件的方法;一個是明天會說的 callapply

作為方法呼叫

函式可以透過寫在物件裡面去呼叫,這個呼叫方式稱作作為方法(method),相信大家都知道,但你知道在這時候的 this 是什麼嗎?

var obj = {
    funA:function(){
        console.log(this);
        console.log(this===obj);
    }
}
obj.funA();
//{funA: ƒ}
//true

可以看到這時候的 thisobj 物件,也是是說我們是透過 obj 去呼叫的。
但是要注意一個小地方:

function funA(){
    console.log(this);
    console.log(this===obj);
}
var obj = {
    a:funA
}
funA();
//Window 
//false

雖然你認為我已經讓 obj 參照 funA 了,但是當你直接執行 funA時,還是由全域環境去呼叫,這也呼應了我們開頭所說的「我們無法在一開始定義它,只有函式呼叫時候才能確定」。

我們可以利用作為方法呼叫來解決作為函式呼叫的問題:

function funA(){
    this.count ++;
}
var obj = {
    count : 0,
    sum : funA
};
obj.sum();
console.log(obj.count);//1

作為建構式呼叫

建構式的函式宣告就跟任何函式一樣,也可以用宣告和表達來建立新物件。
要作為建構式函式來呼叫函式,必須要在函式呼叫前加上 new:

function myConstructor(){
    this.count = 0 ;
    this.sum = function(){
        return this.count ++
    }
}
var a = new myConstructor();
a.sum();
console.log(a.count);

也許有人會覺得,這跟作為方法呼叫很像,那用物件去寫就好何必這麼麻煩還要學建構式,用奇怪的 new 去呼叫函式。

這邊就來說明一下,使用 new 呼叫函式時,會發生什麼事:

  1. 建立一個新物件。
  2. 此物件被當成 this 參數傳遞給建構式,因此成為建構式的函式背景空間。
  3. 回傳新建立的物件。

如果我們今天需要兩個相同的物件,如果用作為物件方法呼叫的話就必須建立兩個不同的物件,否則會參照到同樣的物件並修改。
但是建構式就不需要,因為每一個都是一個新的物件:

function myConstructor(){
    this.count = 0 ;
    this.sum = function(){
        return this.count ++
    }
}
var a = new myConstructor();
var b = new myConstructor();
a.sum();
console.log(b.count); //0

以上就是 this 的基本三種呼叫方式,若有錯誤及參考資料未附上勞請留言。
明天我們會介紹 this 的稍微進階應用 callapply

參考資料:
忍者 JavaScript 開發技巧探秘
你所不知道的JS - this
彻底理解js中this的指向,不必硬背。


上一篇
學JS的心路歷程 Day7-函式(二) arguments
下一篇
學JS的心路歷程 Day9- 函式(四) apply、call
系列文
學JS的心路歷程30

尚未有邦友留言

立即登入留言