Andy's blog

If you always do what you've always done, you'll always get what you've always got.

0%

2021-08-04-[筆記]this:call、apply、bind

前言:
callapplybind 這三個方法可以改變 Function 內的動態 this


參考資料:
JavaScript 核心篇
[JavaScript] 函數原型最實用的 3 個方法 — call、apply、bind
JavaScript - call,apply,bind
[筆記] 了解function borrowing和function currying ─ bind(), call(), apply() 的應用


使用時機:

都是用再明確指定 this 的時候。

Call

1
2
3
4
5
6
7
8
9
10
11
let family ={
name:"小明",
age:25
}

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

fn(1,2); // window,1,2
fn.call(family,3,4); // {name: "小明", age: 25} 3 4

我們可以看到透過 call 方法,我們將函式內的 this 取代為外部傳入的 family 物件

Apply

call 差異只在於 apply 必須傳入陣列,如過不是傳入陣列資料則會顯示下面錯誤訊息

1
2
3
4
5
6
7
8
9
10
11
let family ={
name:"小明",
age:25
}

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

fn.apply(family,[5,6]); // {name: "小明", age: 25} 5,6
// fn.apply(family, 6,7); // Uncaught TypeError: CreateListFromArrayLike called on non-object

Bind

與上面兩者差異在於 Bind 是回傳一個回呼函式

1
2
3
4
5
6
function fn(para1,para2){
console.log(this,para1,para2);
}

let temp = fn.bind(family,7,8);
temp(); //{name: "小明", age: 25} 7 8

差異

  • apply 用在當函式傳入數量不固定,且是陣列
  • bind 會回傳一個包裹函式,當我們執行這個函式時,同時也會將帶入 bind 的 arguments 一起帶進 Function 中。

補充

在非嚴格模式下,nullundefined 將會被置換成全域變數,反之則不會

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 嚴格模式
"use strict";

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

fn.call(null,3,4); // null,3,4
fn.apply(undefined,[5,6]); // undefined,5,6
let temp = fn.bind(null,7,8); // null,7,8
temp();

// 非嚴格模式
function fn(para1,para2){
console.log(this,para1,para2);
}

fn.call(null,3,4); // window,3,4
fn.apply(undefined,[5,6]); // window,5,6
let temp = fn.bind(null,7,8); // window,7,8
temp();

實際應用場景

  1. 我們在取 callBack Function 的 this 時候,都會拿不到正確的 this。
1
2
3
4
5
6
7
8
9
10
11
12
13
var vm = "全域";

var family = {
vm: "小明",
callName: function () {
console.log('1',this);
// 其呼叫方式的是直接在全域下呼叫,所以不管所定義的位置在哪裡,都會指向全域
setTimeout(function () {
console.log(this,this.vm); // 全域
}, 1000);
},
};
family.callName();
  1. 過往作法,是在外層直接指定 const self = this
1
2
3
4
5
6
7
8
9
10
11
12
13
var vm = "全域";
var family = {
vm: "小明",
callName: function () {
const self = this;
console.log('1',self );
setTimeout(function () {
console.log(self,self.vm); // 小明
}, 1000);
},
};
family.callName();

  1. 直接使用 bind 綁定
1
2
3
4
5
6
7
8
9
10
11
12
var vm = "全域";

var family = {
vm: "小明",
callName: function () {
console.log('1',this);
setTimeout(function () {
console.log(this,this.vm); // 小明
}.bind(this), 1000);
},
};
family.callName();