iT邦幫忙

2022 iThome 鐵人賽

DAY 8
2

前言

在 JavaScript 中,我們知道一個函式在宣告後,可以在不同的 execution context 中呼叫使用,例如在全域宣告了 A、B 兩個函式,可以在 B 函式內呼叫 A,被呼叫的 A 要知道當前 execution context 的話,就必須設計一個東西讓它去取得,而這也牽涉到為什麼要設計 this 關鍵字

this 會在創建 execution context 時被綁定,不會指向一個固定的值,它和一般變數、函式不同,不適用 Scope 的概念去判斷它的值,用來指向函式執行时所在的環境,因此這篇文章中要來針對 this 的指向值做個釐清,瞭解在各種情況下 this 會指向哪裡。


this 在各種情況下的指向的值

1. 一般函式/全域中的 this(Default Binding)

上篇提到的 Global Execution Context,也就是全域的執行環境中,this 會指向全域物件 window。

範例:

console.log(this); // window object

function a() {
  console.log(this); // window object
  console.log(this === window); // true
}

a();

以上是沒有設定嚴格模式的情況,但 this 在嚴格模式下執行以下程式會是這樣:

console.log(this); // window object

function a() {
  console.log(this); // undefined
  console.log(this === window); // false
}

a();

非嚴格模式,node.js 底下是 global。

2. 透過物件的方法調用 this(隱式綁定、Implicit Binding)

這種情況下,this 會指向於調用包含 this 的該函式之物件,也就是指向調用者,第一點我們也可以想成調用者是 window。

const person = {
  name: "Jay",
  age: 24,
  showThis() {
    console.log(this);
  }
};

person.showThis(); // person object
const showThisFunc = person.showThis; // 只是傳送記憶體位置給 showThisFunc 變數
showThisFunc(); // window object

3. call()、apply()、bind() 中的 this(顯式綁定、Explicit Binding)

在這三個函式傳入的第一個參數,都會變成 this 的指向。

註: bind() 是產生的新函數會與 bind 的第一個參數綁定起來

範例1:

const name = "Ray";

const person = {
  name: "Jim",
  age: 20,
  sayHi: function() {
    console.log(this); // window object
    console.log("hi " + this.name); // hi
  }.bind(window),
  sayHiNoBind: function() {
    console.log(this); // person object
    console.log("hi " + this.name); // hi Jim
  },
};

person.sayHi();
person.sayHiNoBind();

範例2:

const obj = { a: 'Custom' };

// 此屬性a為全域物件
const a = 'Global';

function whatsThis(arg) {
  console.log(this.a);  // this 值取決於此函數如何被呼叫
}

whatsThis();          // undefined
whatsThis.call(obj);  // Custom,this 指向 obj 物件

4. new 關鍵字調用 this

當一個函式被以 new 的方式呼叫時,會創造一個新的物件,this 指向被創造的那個物件。

function Person(name, age) {
  this.name = name;
  this.age = age;
  console.log(this);
}

const person1 = new Person("Tom", 25);
// 印出 Person { name: 'person1', age: 55 }

5. 事件觸發函式內的 this

此時 this 所指向的則是該 DOM 元素,所以會看到 showMessage 函式裡 this === e.target 為 true。

至於為什麼 showMessage 沒有傳入參數卻能接收到 event?

是因為當使用者觸發 click 時,瀏覽器會產生 event 物件,addEventListener 傳入的 callback function 會接收到該 event 當作參數,可參考 Callback 在 EventListener 的運作

// HTML: <button>Click</button>

class Printer {
  message = 'This works!';

  showMessage(e) {
    console.log(this.message); // undefined
    console.log(this === e.target); // true
  }
}

const p = new Printer();

const button = document.querySelector('button');
button.addEventListener('click', p.showMessage);

調整:

button.addEventListener('click', p.showMessage.bind(p));

6. Callback function & this

由於 callback function 在不同的執行環境執行,所以 this 會有和在建構函式內 this 指向不一樣的情況,不過可以透過 bind()、箭頭函式改善。

function Person(name, age) {
  this.name = name;
  this.age = age;
  setTimeout(function() {
    console.log(this); // 印出 window
  }, 100)
//   setTimeout(() => {
//     console.log(this); // 印出 person1 物件
//   }, 100)
//   setTimeout(function() {
//     console.log(this); // 印出 person1 物件
//   }.bind(this), 100)
}

const person1 = new Person("Tom", 25);

setTimeout 用在這裡可能有點奇怪,但就只是做個 Callback function 的舉例


this 的作用

上面講了很多 this 在各種狀況下指向的值,那它們可以怎麼運用呢,常見的作用有以下幾點:

1. 讓函式可以取得到它自己的物件

2. 可以在不同的物件間執行相同的函式

第一點在上個段落 this 的第二種指向(透過物件的方法調用 this)已經有說過,這邊舉例第二點。

例如以下有兩個物件,obj1 和 obj2 它們都可以使用 showName 函式。

function showName() {
  console.log(this.name);
}

const obj1 = { name: 'Chloe', showName };

const obj2 = { name: 'Make', showName };

obj1.showName(); // Chloe
obj2.showName(); // Make

3. 另外,還可以透過 this 去做 function chaining

此段程式的 this 為 ladder 物件,function chaining 可以讓程式碼看起來更加簡潔。

let ladder = {
  step: 0,
  up() {
    this.step++;
    return this;
  },
  down() {
    this.step--;
    return this;
  },
  showStep() {
    console.log(this.step);
    return this;
  }
};

ladder.up().up().down().showStep().down().showStep(); // 印出 1 然後再印出 0

這次關於 this 的介紹就到這邊,舉了六種 this 指向的情況,不過因為篇幅關係,還沒介紹箭頭函式調用 this 時的情況,將會於明天的文章做介紹。


參考資料 & 推薦閱讀

this - JavaScript | MDN

[教學] JavaScript this 用法整理


上一篇
Day7-閉包(Closure)介紹
下一篇
Day9-箭頭函式與 this
系列文
強化 JavaScript 之 - 程式語感是可以磨練成就的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

1
json_liang
iT邦研究生 5 級 ‧ 2022-09-08 11:25:20

感謝分享 this 概念,這個真的很重要

1
雷N
iT邦研究生 1 級 ‧ 2022-09-08 12:43:08

開始期待有TypeScript

harry xie iT邦研究生 1 級 ‧ 2022-09-08 15:53:51 檢舉

哈哈,這次沒要寫 TS

我要留言

立即登入留言