Understand JavaScript #18 bind()、call() 與 apply()
2021-04-08
本文主要內容為探討 bind()、call() 與 apply() 的相關知識,這三個函式都與 this 有關,可以用來控制 this 變數。
前言
函式物件除了有 CODE 與 NAME 這兩個屬性之外,還有 bind、call 與 apply 這三個方法,這三個方法與 this 以及傳入函式的參數有關。
在開始介紹前,我們先用物件實體語法建立一個物件 person
,以下會使用這個物件作為範例。
var person = {
firstname: 'Sealman',
lastname: 'Huang',
getFullName: function () {
var fullName = `${this.firstname} ${this.lastname}`;
return fullName;
},
};
這裡的 this 關鍵字會指向包含該方法的物件,也就是
person
。
bind()
.bind()
會創造一份你要呼叫的函式的拷貝(沒有執行函式)- 傳入的參數就是 this 指向的東西
例如:如果透過 this 去取用物件中的 getFullName
會出現錯誤,因為此時的 this 是指向全域物件。
var logName = function (lang1, lang2) {
console.log(`Logged: ${this.getFullName()}`);
};
logName(); // Uncaught TypeError: this.getFullName is not a function
我們可以用 bind()
控制 this 的指向,強迫讓 this 指向 person
物件。
使用上,不是去呼叫 logName
函式,而是取用 logName
這個函式物件裡的 bind 方法,寫成 logName.bind()
,然後傳入想要讓 this 變數指向的物件。
使用後,bind(person)
會複製前面的 logName
函式物件,當 JavaScript 引擎看到傳入括號裡的 person
物件時,就會判斷 this 要指向這個物件。
var logName = function (lang1, lang2) {
console.log(`Logged: ${this.getFullName()}`);
console.log(`Arguments: ${lang1} ${lang2}`);
};
var logPersonName = logName.bind(person);
logPersonName('zh-tw', 'en');
// Logged: Sealman Huang
// Arguments: zh-tw en
我們也可以在創造函式時立刻接著寫上 .bind(person)
,這樣就不用額外建立一個變數哩。
var logName = function (lang1, lang2) {
console.log(`Logged: ${this.getFullName()}`);
}.bind(person);
logName(); // Logged: Sealman Huang
call()
- 由
.call()
做執行的動作,而不是執行前面接的函式 - 第一個參數是 this 變數指向的物件
- 剩下的參數是傳給函式的參數
var logName = function (lang1, lang2) {
console.log(`Logged: ${this.getFullName()}`);
console.log(`Arguments: ${lang1} ${lang2}`);
};
logName.call(person, 'zh-tw', 'en');
// Logged: Sealman Huang
// Arguments: zh-tw en
另外,我們也可以在創造函式後立刻執行,有點類似立即函式的概念,但這邊不是用 IIFE 執行函式的,而是用 .call()
來完成。
(function (lang1, lang2) {
console.log(`Logged: ${this.getFullName()}`);
console.log(`Arguments: ${lang1} ${lang2}`);
}.call(person, 'zh-tw', 'en'));
apply()
- 與
.call()
做的事情幾乎相同,差別在於第二個傳入的參數必須是「陣列」型別 - 第一個參數是 this 變數指向的物件
- 第二個參數是傳給函式的參數,型別必須為陣列
logName.apply(person, ['zh-tw', 'en']);
// Logged: Sealman Huang
// Arguments: zh-tw en
陣列在進行數學運算時比較方便,像是陣列內的數字相加,所以使用 call 還是 apply 就看使用函式的情況。
另外,與 call 一樣,apply 也可以在創造函式後立刻呼叫它。
(function (lang1, lang2) {
console.log(`Logged: ${this.getFullName()}`);
console.log(`Arguments: ${lang1} ${lang2}`);
}.apply(person, ['zh-tw', 'en']));
實際應用
講了這麼多,我們在真實生活中到底什麼時候會用到這些東西呢?
函式借用 (Function Borrowing)
- 函式借用:借用其他物件的方法
假設我們有另一個類似 person
的物件叫做 person2
,它的值不同,也沒有 getFullName
方法。
如果我想要用 person2
作為 getFullName
方法裡的 this