this 在函式中,主要是看誰呼叫這個函式

this 的四套規則:

  1. 預設繫結
  2. 隱含的繫結
  3. 明確的繫結
  4. new 繫結

預設 binding

預設繫結為 global object,當其他規則都不符合的時候
就會使用此規則

1
2
3
4
5
function foo() {
console.log(this.a)
}
var a = 'global object'
foo() //'global object';

如果使用 const a = 'global object';,則會印出 undefined,因為 const 並不會讓 a 掛在 global object 下, 如果使用 window.a = 'global object',結果則和 var 一樣。
使用嚴格模式 use strict 則會得到 TypeError

隱含的 binding

是否有一個物件去 call 那個 function

1
2
3
4
5
6
7
8
9
10
function foo() {
console.log(this.a)
}

const obj = {
a: 2,
foo: foo
}

obj.foo() //2

使用 callback 的方式呼叫,也會因為呼叫的地點而將繫結斷開。
如下所示, printA 已經不是被 obj 所呼叫,
因此一樣使用預設繫結。

1
2
3
4
5
6
7
8
9
10
11
12
13
function printA() {
console.log(this.a)
}

function callPrintA(fn) {
fn()
}
const obj = {
a: 2,
printA: printA
}
var a = 'global object A'
callPrintA(obj.printA) //'global object A'

隱含的失去

因為呼叫是 bar 呼叫 obj 內的 printA,(行為委派)
因此採用的是 global 下的 global object ,而不是印出 2 。

1
2
3
4
5
6
7
8
9
10
function printA() {
console.log(this.a)
}
const obj = {
a: 2,
printA: printA
}
const bar = obj.printA
var a = 'global object'
bar() //"global object"

由於是 bar() 呼叫了 fn printA 所以,是一個普通的呼叫,使用了預設繫結

明確的 binding

使用 call()apply()bind()

call()

使用 call() , this 會指定在物件上(有給第一個參數)(改變 this 指向)

this 綁定 obj,在函式 bar 讓 this 明確的綁住 obj ,不管怎麼控制 bar 的綁定, this 都會是 obj。

1
2
3
4
5
6
7
8
9
10
function foo() {
console.log(this.a)
}
var obj = { a: 2 }
var bar = function() {
foo.call(obj)
}
bar() //2
setTimeout(bar, 100) //2
bar.call(window) //2

apply()

apply()call() 大致上雷同,唯一不同是 call() 可以接受一連串的參數, apply() 則接受一組陣列形式的參數。

bind()

bind() 會回傳一個 function,而這個 function, 經過處理, this 已經被綁定在指定的 obj 上。

1
2
3
4
5
6
function foo() {
console.log(this.a)
}
var obj = { a: 2 }
var bar = foo.bind(obj)
bar() //2

由於 bind() return 一個 function,因此如果有參數,可以在 bar() 再帶參數。
如下,可以想成加工(強制 this 綁定在 obj)後的 addToThis

1
2
3
4
5
6
7
8
obj = {
num: 2
}
function addToThis(a, b, c) {
return this.num + a + b + c
}
var bar = addToThis.bind(obj)
bar(1, 2, 3) //8

new binding

無論是 function constructor 或 classes ,
使用 new 會創造出一個全新的物件, this 即指向這個物件。

1
2
3
4
5
function People(name) {
this.name = name
}
var peter = new People('Peter')
console.log(peter.name) //Peter

reference

You Don’t know JS: Scope & Closure, this & Prototypes