iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0
Modern Web

超低腦容量學習法遇到javascript系列 第 7

有關這個(this)的二三事(上)

  • 分享至 

  • xImage
  •  

this是js裡面一個特別的關鍵字,有關this的第一件事就是,它是一個動態的值,依照各種函式呼叫的情境不同而指向不同的東西。

四個呼叫函式的情境

1. method

呼叫obj裡的方法,this指向呼叫method的物件
下面例子當中的this就是指向物件john

const john = {
  firstName: "John",
  greet: function () {
    console.log(this);
    console.log(`Hi, I'm ${this.firstName}.`);
  },
};

john.greet();

執行結果:

需要注意的是,上面例子的定義method和呼叫method都是同一物件,容易造成誤會this是指當初定義method的物件,但其實不是。
下面的例子從借用第一個物件的方法,新增到第二個物件裡,但在使用第二個物件呼叫method時,this是第二的物件

const emma = {
  firstName: "Emma",
};

emma.greet = john.greet; //借用john物件裡的method, 新增到emma物件裡當method
console.log(emma.greet); //內容與john物件裡的相同
emma.greet();

執行結果:

2. simple function call

當呼叫一般函式時,函式裡的this有兩種可能性:

  • 在嚴格模式下,this=undefined
    下面使用之前的例子,不過把物件的method存到另一個變數作為一個普通的函式,後續再呼叫這個函式的時候,因為此時我們做的事是呼叫一個普通的函式,故this為undefined,而undefined呼叫method則產生錯誤:
    "use strict";
    const john = {
      firstName: "John",
      greet: function () {
        console.log(this);
        console.log(`Hi, I'm ${this.firstName}.`);
      },
    };
    
    const simpleFunc = john.greet;
    simpleFunc();
    

  • 在一般模式下,this=window物件
    下面的例子跟上面很像,就取消了嚴格模式,並新增了一個var宣告,卻有完全不一樣的結果

    var firstName = "OMG!😱";
    const john = {
      firstName: "John",
      greet: function () {
        console.log(this);
        console.log(`Hi, I'm ${this.firstName}.`);
      },
    };
    
    const simpleFunc = john.greet;
    simpleFunc();
    

    執行結果:

    再來一個把var改成const會變這樣:

    const firstName = "OMG!😱";
    const john = {
      firstName: "John",
      greet: function () {
        console.log(this);
        console.log(`Hi, I'm ${this.firstName}.`);
      },
    };
    
    const simpleFunc = john.greet;
    simpleFunc();
    

    執行結果:

    有感覺到頭暈了嗎?但其實只要知道下面兩件事,說穿了根本也沒什麼大不了,一個就是在一般模式之下,simple function call的this指向window物件,再來就是var宣告會再window物件新增一個以變數為名的屬性(而const和let並不會),把window物件點開往下滑,仔細找找會看到下圖,所以this.firstName會抓到window物件的屬性

3.arrow function

arrow function沒有自己的this,當在arrow function裡使用this時,會指向它父層的this。
回到一開始的例子,但把method改成arrow function宣告,神奇的事又發生了...因為arrow function會抓父層的this,這邊的父層是全域,也就是抓到的是window物件,而其中並沒有firstName這個屬性。

const john = {
  firstName: "John",
  greet: () => {
    console.log(this);
    console.log(`Hi, I'm ${this.firstName}.`);
  },
};

john.greet();


那如果剛好在全域有一個用var宣告的firstName,那就會出現難以發覺的bug,而原因就像之前的例子說過的,var會在window物件新增屬性。所以如果避免用arrow function宣告methhod,就可以避免這件事。

var firstName = "OMG!😱";
const john = {
  firstName: "John",
  greet: () => {
    console.log(this);
    console.log(`Hi, I'm ${this.firstName}.`);
  },
};

john.greet();

4.event listener

而event listener裡的callback function若有使用this關鍵字,它所指的則是被監聽的那個物件。
下面我們把最一開始的例子,加上一個按鈕並設定若它被點擊會跳出一個alert視窗,視窗中的字串我們用了this。其中this指向button element而this.firstName則為undefined。

const john = {
  firstName: "John",
  greet: function () {
    const btnEl = document.querySelector("button");
    btnEl.addEventListener("click", function () {
      console.log(this);
      alert(`Hi, I'm ${this.firstName}.`);
    });
  },
};

執行結果:

有關this的規則整理

各種this 指向什麼
method裡的this 呼叫method的物件
regular function裡的this 嚴格模式下=>undefined; 一般模式下=>window物件
arrow function裡的this 父層的this
event listener裡的this 監聽的元素

今日總結

從上面的表格能發現,一般函式的this指向undefined或window物件,似乎會造成一些困擾。下篇會整理一些例子與應用上的注意事項,see you then.

ref
關於 JavaScript 的 this 各種用法與探討


上一篇
理解遞迴
下一篇
有關這個(this)的二三事(下)
系列文
超低腦容量學習法遇到javascript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言