今天我在學習手寫 Promise
的過程中又遇到了 this
。說實話,作為從業三年的開發者,this
一直是個讓我頭疼的問題,尤其對於我這個用了三年的 React 函式式元件的人來說,this
的指向總是變來變去。我常常困惑於它到底指向了什麼、為什麼要用它。可是,當我嘗試繼續深入理解 Promise
時,發現繞不開 this
這個概念。沒辦法,只能硬著頭皮學下去了。既然如此,我們就一起來揭開這個在 JavaScript 中根深蒂固的“謎題”。
this
是 JavaScript 中一個複雜且重要的概念,它的指向是動態的,取決於函式的呼叫方式。要徹底理解 this
,我們需要深入瞭解它在不同場景下的表現。接下來,我將透過幾個不同的例子幫助你全面理解 this
的使用。
1. 普通函式中的 this
在普通函式中,this
的指向取決於誰呼叫了這個函式,它在呼叫階段動態繫結。例如:
function showThis(){ console.log(this); // 瀏覽器中是 window,Node.js 中是 global } showThis();
在這個例子中,showThis
是在全域性環境中執行的,相當於全域性環境呼叫了這個函式,因此 this
的指向是全域性物件。
如果在物件的方法中呼叫函式,呼叫者是該物件,那麼 this
的指向就是該物件:
const obj = { a: 1, b: function(){ console.log(this.a); // 輸出 1 } } obj.b();
注意:在嚴格模式 ('use strict'
) 下,如果 this
沒有顯式繫結,它的指向會是 undefined
:
'use strict'; function showThis(){ console.log(this); // 輸出 undefined } showThis();
2. 箭頭函式中的 this
箭頭函式與普通函式不同,它的 this
在定義時就已經確定了。箭頭函式不會建立自己的 this
,而是繼承自定義時所在的作用域。看看下面的例子:
function Outer(){ this.name = 'outer'; const regularFunction = function(){ console.log(this.name); // 非嚴格模式下為 undefined,嚴格模式下同樣為 undefined } const arrowFunction = () => { console.log(this.name); // 輸出 'outer' } regularFunction(); arrowFunction(); } new Outer();
在這個例子中,箭頭函式 arrowFunction
繼承了 Outer
的作用域,因此 this
指向 Outer
的例項。而普通函式 regularFunction
沒有繼承 Outer
的 this
,所以 this.name
輸出 undefined
。
物件中的箭頭函式也是類似的:
const obj = { a: 1, b: function(){ const output = () => { console.log(this.a); // 輸出 1 } output(); } } obj.b();
在這個例子中,箭頭函式 output
繼承了 b
方法的 this
,因此 this.a
輸出的是 obj.a
。
3. 顯式繫結:call
、apply
和 bind
上面提到的例子都是隱式繫結,即 this
自動繫結到呼叫者。接下來我們看看顯式繫結,即透過 call
、apply
和 bind
來手動控制 this
的指向。
1. call()
和 apply()
call
和 apply
都會立即呼叫函式,它們的區別在於引數傳遞方式不同:
call
:引數以逗號分隔傳遞。apply
:引數以陣列形式傳遞。
例如:
function greet(greeting, punctuation) { console.log(greeting + ', ' + this.name + punctuation); } const person = { name: 'Bob' }; greet.apply(person, ['Hi', '.']); // 輸出 "Hi, Bob." greet.call(person, 'Hello', '!'); // 輸出 "Hello, Bob!"
2. bind()
bind
和 call
、apply
不同,它不會立即呼叫函式,而是返回一個新函式,這個新函式的 this
永遠繫結到 bind()
的第一個引數。你可以傳遞引數給 bind()
,這些引數會在呼叫新函式時依次傳入。
例如:
function greet(greeting, punctuation) { console.log(greeting + ', ' + this.name + punctuation); } const person = { name: 'Charlie' }; // 使用 bind 建立一個新的函式,this 永遠繫結到 person const boundGreet = greet.bind(person, 'Hey'); // 呼叫繫結的函式 boundGreet('!!'); // 輸出 "Hey, Charlie!!"
總結
this
是 JavaScript 中一個動態的概念,它的指向取決於函式的呼叫方式。在普通函式中,this
的指向取決於誰呼叫了該函式;而在箭頭函式中,this
會繼承定義時的上下文。此外,透過 call
、apply
和 bind
,我們可以顯式地控制 this
的指向。理解 this
是掌握 JavaScript 高階特性的關鍵,尤其是在編寫複雜的非同步程式碼和元件庫時。
透過這些場景的例子,你應該能夠更好地理解 this
在 JavaScript 中的表現,進而更加自信地在專案中使用它!