BACK
Featured image of post 使用 bind、call、apply 改變 this 指向的對象

使用 bind、call、apply 改變 this 指向的對象

如果想要改變 this 指向的對象,可以透過 bind、call、apply 這三個 method 辦到,本篇來講解一下關於這三個 method 的差異。

參考網站
參考網站
參考網站
參考網站
參考網站


使用 bind、call、apply 改變 this 指向的對象

如果想要改變 this 指向的對象,可以透過 bindcallapply 這三個 method 辦到。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const person = {
  userName: 'Blueberry'
}

// 接著 create 一個函式來呼叫 person 的 userName:  
function callName() {
  console.log('Hello ' + this.userName);
}

// 呼叫 callName: 
callName();
output:
1
> Hello undefined

因為 callName() 函式中的 this 指向的是 global object(也就是 Window 物件),所以這時候我們希望把 callName() 的 this 指向 person 這個對象,可以使用以下方法:


bind

MDN web docs

1
function.bind(thisArg[, arg1[, arg2[, ...]]])

bind 和其他兩個方法(callapply)的不同有兩點:

  • bind創造一個函式物件的拷貝不會執行函式,因此 bind 之後還要再另外寫執行函式的動作;而 callapply直接執行函式。
  • bind 後面傳入的參數值會設定為拷貝函式的永久參數值,之後執行拷貝函式時,無論怎麼給予參數都沒有用;而 callapply 則是單純給予參數,像一般呼叫函式那樣。

我們先單純就第一點來說明,示範最基本的 bind 怎麼寫。

第一種寫法

1
callName.bind(person)();

也等於:

1
2
const callFunction = callName.bind(person);  // 創造函式物件的拷貝
callFunction();  // 執行函式
output:
1
> Hello Blueberry

第二種寫法,直接寫在函式表示式後面也可以

1
2
3
4
5
const callName = function() {
  console.log('Hello ' + this.userName);
}.bind(person);

callName();
output:
1
> Hello Blueberry

接下來針對第二點來說明,先來改寫一下 callName 函式,加入兩個 arguments:

1
2
3
4
5
function callName(age, interest) {
  console.log('Hello ' + this.userName);
  console.log('Your age is ' + age);
  console.log('Your interest is ' + interest);
}

接著我們除了要用 bindthis 指向 person 之外,還要給予 ageinterest 這兩個參數。

這邊有兩種做法:單純給定參數,或綁定永久參數值

單純給定參數

要單純給定參數的話,像一般執行函式那樣,在執行函式時再給予參數就可以了。

1
2
const callFunction = callName.bind(person);
callFunction(24, 'Reading books');
output:
1
2
3
> Hello Blueberry
> Your age is 24
> Your interest is Reading books

綁定永久參數值

如果將參數放在 bind 中,這個函式拷貝物件的參數值就會永遠被固定住。

1
2
const callFunction = callName.bind(person, 24, 'Reading books');
callFunction(30, 'Playing games');  // 這邊無論再怎麼給參數都沒用
output:
1
2
3
> Hello Blueberry
> Your age is 24
> Your interest is Reading books

call

MDN web docs

1
function.call(this, arg1, arg2..., argn)

callbind 不同,它會直接執行函式,後面給的參數也不會被固定住。

.call() 存在於任何一個函數或者方法上,是個 function 就可以調用 .call()

1
callName.call(person, 24, 'Reading books');
output:
1
2
3
> Hello Blueberry
> Your age is 24
> Your interest is Reading books

修改 this 指向

  • 對於沒有定義在任何對像上的 function,實際上也有 this 指向,指向的是個全局對象
  • 對於定義在某個 object 上面的方法,this 指向,很好理解。

下面拿一個對象變數做例子,來說明問題:

1
2
3
4
5
6
7
8
let sunan = {
  c: 3,
  test: function (a, b) {
    return a + b + this.c;
  }
}
console.log(sunan.test(1, 2));                 // 6
console.log(sunan.test.call({ c: 4 }, 1, 2));  // 7

在這個例子裡面,.call() 的第一個參數傳入了一個新的對象,它覆蓋了原有的數據。c 屬性由 3 變成了 4,而 test 方法依然存在。

output:
1
2
> 6
> 7

apply

MDN web docs

1
function.apply(this, [arg1, arg2..., argn])

apply 的寫法跟 call 很相近,與 call 不同的是,後面的參數需要使用陣列傳遞,適合搭配 arguments 運用在算數的函式。

1
callName.apply(person, [24, 'Reading books']);
output:
1
2
3
> Hello Blueberry
> Your age is 24
> Your interest is Reading books

同一個例子的 call 與 apply 寫法比較

1
2
callName.call(person, 24, 'Reading books');
callName.apply(person, [24, 'Reading books']);

修改 this 指向

  • .call() 一樣,.apply() 的第一個參數也是修改 this 指向的。

可能的錯誤

也許會碰到下面的錯誤提示信息:

1
TypeError: CreateListFromArrayLike called on non-object

解決方案就是把參數打包成數組[],進行傳遞。


使用情境

function borrowing: 借用 function

function borrowing 就是別人函式中的方法來用的意思,下面示範 somebody 借用 person 的 getUserName 方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const person = {
  userName: 'Blueberry',
  getUserName: function() {
    console.log(this.userName);
  }
}

const somebody = {
  userName: 'Fan'
}

// with bind()
const newUser = person.getUserName.bind(somebody);
newUser();

// with call()
person.getUserName.call(somebody);

// with apply()
person.getUserName.apply(somebody);
output:
1
2
3
> Fan
> Fan
> Fan

function currying: 拷貝 function

function currying 的定義為建立一個函式的拷貝,並設定預設的參數,這在數學運算下很有用。下面我們就利用 bind 的特性來完成 function currying。

1
2
3
4
5
6
const mutiply = function (a, b) {
  return a * b;
}

const mutiplyByTwo = mutiply.bind(this, 2);
console.log(mutiplyByTwo(4));
output:
1
> 8

上面我們建立了一個函式 mutiply(a, b),並用 bind 建立函式物件拷貝 mutiplyByTwo()

mutiply.bind(this, 2) 這邊的 this 並不重要,因為函式裡沒有使用到 this

而後面的 2 則是永久綁定了參數 a

為了讓程式碼比較好理解,這邊將上面那段程式碼拆解,它也等於:

1
2
3
4
5
6
7
8
const mutiply = function (a) {
  return (b) => {
    return a * b;
  }
}

const mutiplyByTwo = mutiply(2);
console.log(mutiplyByTwo(4));
output:
1
> 8
comments powered by Disqus