0%

JS 核心觀念筆記 - this 的呼叫方式:call、apply、bind

this

Simple Call

簡易呼叫已經知道將 this 指向全域 window 的物件,下方程式碼為範例。

1
2
3
4
5
6
7
8
9
10
11
var vm = "全域";

var family = {
vm: "小明",
};

function home(para1, para2) {
console.log(this, para1, para2);
}

home(1, 2);

呼叫 home 這個函式並帶入參數內容,會得到一個全域物件跟帶入的參數值。

callback

call

使用 call 的方法來呼叫 this 的話,可以改這樣寫。

1
2
3
4
5
6
7
8
9
10
var vm = "全域";

var family = {
vm: "小明",
};

function home(para1, para2) {
console.log(this, para1, para2); //小明,3,4
}
home.call(family, 3, 4);

呼叫 home 函式,並使用 call 的方法,將 family 物件取代 this,這個 this 就會指向 family,所以得到的值就會是 family 裡面的 vm。

apply

與 call 的方式相似,只是呼叫時帶入參數的格式不同,是用陣列表示。

1
2
3
4
5
6
7
8
9
10
var vm = "全域";

var family = {
vm: "小明",
};

function home(para1, para2) {
console.log(this, para1, para2); //小明,5, 6
}
home.apply(family, [5, 6]);

call 跟 apply 的調用方法非常相似,且會立刻執行。

bind

別於上方兩個方法,bind 不會立刻執行,調用參數的方法與 call 相同。

1
2
3
4
5
6
7
8
9
10
11
12
var vm = "全域";

var family = {
vm: "小明",
};

function home(para1, para2) {
console.log(this, para1, para2); //小明,7, 8
}

var otherHome = home.bind(family, 7, 8);
otherHome();

使用 bind 的方法時,要先宣告一個變數,其值為呼叫函式並使用 bind,調用參數的方法與 call 相同,但並不會執行,而是要呼叫剛剛宣告的變數才會執行其函式,並把 this 替換掉。

容易被誤解的地方是呼叫的樣子跟 simple call 很像,但因為在使用 bind 方法時,已經決定呼叫的方法,所以不會跟 simpel call 的結果相同。

因為已經決定呼叫方式,所以在 otherHome 函式中加入參數,並不會改變其結果,但可以部分帶入參數,如下:

1
2
3
4
5
6
7
8
9
10
11
12
var vm = "全域";

var family = {
vm: "小明",
};

function home(para1, para2) {
console.log(this, para1, para2); //小明,9,1
}

var otherHome = home.bind(family, 9);
otherHome(1, 2);

可見到如果 bind 方法只帶入一個參數 9,第二個參數的位置會帶入函式中新帶入的第一個參數 1,新帶入的第二個參數 2,並不會被帶入。

進階觀念

前面 this 都是物件形態出現,這邊透過 call 的方法給予純值的話會變怎麼樣?

1
2
3
4
5
6
7
8
9
10
11
var vm = "全域";

var family = {
vm: "小明",
};

function home(para1, para2) {
console.log(this, para1, para2);
}

home.call(1, "參數1", "參數2");

會看到得到的結果是:

call

this 變成一個建構式的樣子呈現 this,如果使用 typeof(this) 查看,會得到一個 object 的類型。

如果傳入文字,就會得到一個 string 的物件,那如果傳入 undefined,結果則會指向 window 物件。

MDN 的說明:

若這個函數是在非嚴苛模式( non-strict mode ), nullundefined 將會被置換成全域變數,而原生型態的值將會被封裝

嚴格模式 use strict

因為 JavaScript 是弱型別的語言,規則上較為寬鬆,所以使用嚴格模式可以排除一些原本不會顯示的錯誤。

  • 使用 ‘use strict’ 啟用嚴格模式。
  • 對於沒支援嚴格模式的瀏覽器不影響。
  • 可依照要執行嚴格模式的程式碼片段上方加入 ‘use strict’ 這個表達式。
  • 可以排除一些不好的程式撰寫習慣。
  • 不能使用未來被 ECMAScript 定義的關鍵字。

下方使用立即函式做為範例,並給予嚴格模式,來限制其執行環境,如下方程式碼:

1
2
3
4
(function () {
"use strict";

})();

在立即函式中給一個變數與值,結果會跳錯,原因是在嚴格模式下不能直接讓變數賦予值,而是要先宣告一個變數再給予值。

1
2
3
4
(function () {
"use strict";
a = 1; // is not defined
})();

反之,如果把嚴格模式移除,就不會跳錯,但也不會執行。

嚴格模式在不同的瀏覽器所執行的結果會些許不同。

1
2
3
4
5
function callStrict(param01, param02) {
"use strict";
console.log(this, typeof this, param01, param02); //1 "number" "參數1" "參數2"
}
callStrict.call(1, "參數1", "參數2");

嚴格模式下可以看到 console 的結果就不會用建構式的方式呈現,就會按照原本的型別出現,如果 this 用 undefined 傳入也會得到 undefined 的值與型別。

1
2
3
4
5
6
function callStrict(param01, param02) {
"use strict";
console.log(this, typeof this, param01, param02); //1 "number" "參數1" "參數2"
}
callStrict.call(1, "參數1", "參數2"); //1 "number" "參數1" "參數2"
callStrict.call(undefined, "參數1", "參數2"); //undefined "undefined" "參數1" "參數2"

如果在嚴格模式下使用 simple call 會變怎樣?

1
2
3
4
5
6
function callStrict(param01, param02) {
"use strict";
console.log(this, typeof this, param01, param02);
}
callStrict.call(1, "參數1", "參數2"); //1 "number" "參數1" "參數2"
callStrict("參數1", "參數2"); //undefined "undefined" "參數1" "參數2"

盡量不要使用 simple call 的 this 原因

有發現到就跟 undefined 是一樣的結果,所以得知,simple call 的 this 其實指向的就是 undefined,所以盡量不要使用 simple call 的 this 的原因在此。

資料參考

  1. 鐵人賽:JavaScript 的 this 到底是誰?
  2. JavaScript This 系列文:this 為什麼指向 window
  3. JavaScript This 系列文:this 與物件的關係
  4. 重新認識 JavaScript: Day 20 What’s “THIS” in JavaScript (鐵人精華版)