參考網站
參考網站
參考網站
參考網站
參考網站
使用 bind、call、apply 改變 this 指向的對象
如果想要改變 this
指向的對象,可以透過 bind
、call
、apply
這三個 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:
因為 callName() 函式中的 this
指向的是 global object
(也就是 Window
物件),所以這時候我們希望把 callName() 的 this
指向 person
這個對象,可以使用以下方法:
bind
MDN web docs
1
| function.bind(thisArg[, arg1[, arg2[, ...]]])
|
bind 和其他兩個方法(call、apply)的不同有兩點:
- bind 是創造一個函式物件的拷貝,不會執行函式,因此 bind 之後還要再另外寫執行函式的動作;而 call、apply 是直接執行函式。
- bind 後面傳入的參數值會設定為拷貝函式的永久參數值,之後執行拷貝函式時,無論怎麼給予參數都沒有用;而 call、apply 則是單純給予參數,像一般呼叫函式那樣。
我們先單純就第一點來說明,示範最基本的 bind 怎麼寫。
第一種寫法
1
| callName.bind(person)();
|
也等於:
1
2
| const callFunction = callName.bind(person); // 創造函式物件的拷貝
callFunction(); // 執行函式
|
output:
第二種寫法,直接寫在函式表示式後面也可以
1
2
3
4
5
| const callName = function() {
console.log('Hello ' + this.userName);
}.bind(person);
callName();
|
output:
接下來針對第二點來說明,先來改寫一下 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);
}
|
接著我們除了要用 bind 將 this
指向 person 之外,還要給予 age 和 interest 這兩個參數。
這邊有兩種做法:單純給定參數,或綁定永久參數值。
單純給定參數
要單純給定參數的話,像一般執行函式那樣,在執行函式時再給予參數就可以了。
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)
|
call
和 bind
不同,它會直接執行函式,後面給的參數也不會被固定住。
.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:
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:
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:
上面我們建立了一個函式 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: