this、apply、call、bind (轉載)

this、apply、call、bind

學習自(純紀錄自己學了甚麼,非營利)

https://juejin.im/post/6844903496253177863

this 的指向

this 永遠指向最後調用他的對象

我常拿來判斷的想法

看他是被當作對象內的方法調用還是全局函數

直接來看例子比較好懂

例子

1
2
3
4
5
6
7
8
9
10
11
var name = 'Mike'
let foo = {
name: 'Mary',
getName() {
console.log(this.name)
}
}
foo.getName() // 很明顯是Mary,因為this指向的是最後調用他的對象
window.foo.getName() // 這也很明顯是Mary,因為會是最後調用的對象(即foo)
let bar = foo.getName // 記住這是賦與內存地址給他,執行的時候並不會變成 window.foo.getName
bar() // Mike (window.bar())

這句超級重要

// 記住這是賦與內存地址給他,執行的時候並不會變成 window.foo.getName

題外話:

​ 不知道大家有沒有發現我這裡第一行定義是用var來定義,因為如果用let 來定義的話,並不會變成全局變量(window.name),還有這裡如果用嚴格模式會報錯

1
2
3
4
"use strict" 
var name = 'Mike'
let foo = {
... // 底下都一樣

如何改變this指向

箭頭函數(之後會開一篇出來講先pass)

可以避免很多坑this指向最後調用對象的坑,

箭頭函數的this始終指向函數定義時的this,而非執行時。

​ 箭頭函數中沒有this綁定,必須通過查找作用域鏈來決定其值,如果箭頭函數被非箭頭函數包含,則this綁定的是最近一層非箭頭函數的this,否則,this為undefined

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var name = "windowsName";
function func1() {
console.log('我是全局函數!');
}

var a = {
name: "Cherry",

func1: function () {
console.log(this.name)
},

func2: function () {
setTimeout(() => {
this.func1()
}, 100); // Cherry
setTimeout(function () {
this.func1()
}, 100); // 我是全局函數!(因為setTimeout會被window調用)
}
};

a.func2()

注意:

setTimeout中使用普通函數,1秒後函數執行時,這時函數其實是在全局作用域執行

在函數內部使用 _this = this

這裡用了一個很聰明的辦法,就是:

利用_this變量去紀錄調用過程的this對象

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ar name = "windowsName";

var a = {

name: "Cherry",

func1: function () {
console.log(this.name)
},

func2: function () {
var _this = this; // 這裡在調用時會是a這個對象
console.log(this); // {name: "Cherry", func1: ƒ, func2: ƒ}
setTimeout(function () {
_this.func1()
}, 100);
}
};

a.func2() // Cherry

使用apply、call、bind

先來看例子晚點講原理

可以發現其實三個的用法幾乎一樣,只有bind要再執行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var a = {
name: "Cherry",

func1: function () {
console.log(this.name)
},

func2: function () {
// apply
setTimeout(function () {
this.func1() // Cherry
}.apply(a), 100);

// call
setTimeout(function () {
this.func1() // Cherry
}.call(a), 100);

// bind
setTimeout(function () {
this.func1() // Cherry
}.bind(a)(), 100);

}
};

a.func2()

apply、call、bind 區別

apply以及call差別

基本上是沒差,只是傳遞參數apply是用array而call是分批傳入

定義:

fun.apply (thisArg,[argsArray])

fun.call (thisArg[, arg1[, arg2[, …]]])

看起來有夠複雜,其實就是最上面講得那句。直接看例子就懂了

例子:

1
2
3
4
5
6
7
8
9
10
var a ={
name : "Cherry",
fn : function (a,b) {
console.log(a + b)
}
}

var b = a.fn;
b.apply(a,[1,2]) // 3
b.call(a,1,2) // 3

bind 和apply、call 區別

bind跟他們兩個的最大區別就是

bind是創建函數,還需要手動調用

1
2
3
4
5
6
7
8
9
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}

var b = a.fn;
b.bind(a,1,2)() // 3