我喜歡在JS中更改函數執行上下文的指向,也稱為 this 指向。
例如,咱們可以在類別數組物件上使用數組方法:
const reduce = Array.prototype.reduce; function sumArgs() { return reduce.call(arguments, (sum, value) => { return sum += value; }); } sumArgs(1, 2, 3); // => 6
另一方面,this 很難掌握。
咱們常常會發現自己用的 this 指向不正確。下面的教你如何簡單地將 this 綁定到所需的數值。
【相關課程推薦:JavaScript影片教學】
在開始之前,我需要一個輔助函數execute(func),它只執行作為參數提供的函數。
function execute(func) { return func(); } execute(function() { return 10 }); // => 10
現在,繼續理解圍繞this錯誤的本質:方法分離。
1. 方法分離問題
假設有一個類別Person包含欄位firstName和lastName。此外,它還有一個方法getFullName(),該方法傳回此人的全名。如下圖所示:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { this === agent; // => true return `${this.firstName} ${this.lastName}`; } } const agent = new Person('前端', '小智'); agent.getFullName(); // => '前端 小智'
可以看到Person函數作為建構子被呼叫:new Person('前端', '小智')。函數內部的 this 表示新建立的實例。
getfullname()傳回此人的全名:'前端 小智'。如預期的那樣,getFullName()方法內的 this 等於agent。
如果輔助函數執行agent.getFullName方法會發生什麼:
execute(agent.getFullName); // => 'undefined undefined'
執行結果不正確:'undefined undefined',這是 this 指向不正確導致的問題。
現在在getFullName() 方法中,this的值是全域物件(瀏覽器環境中的 window )。 this 等於 window,${window.firstName} ${window.lastName} 執行結果是 'undefined undefined'。
發生這種情況是因為在呼叫execute(agent.getFullName)時該方法與物件分離。基本上發生的只是常規函數呼叫(不是方法呼叫):
execute(agent.getFullName); // => 'undefined undefined' // 等价于: const getFullNameSeparated = agent.getFullName; execute(getFullNameSeparated); // => 'undefined undefined'
這個就是所謂的方法從它的物件中分離出來,當方法被分離,然後執行時,this 與原始物件沒有連接。
1、為了確保方法內部的this指向正確的對象,必須這樣做
2、以屬性存取器的形式執行方法:agent.getFullName()或靜態地將this綁定到包含的物件(使用箭頭函數、.bind()方法等)
方法分離問題,以及由此導致this指向不正確,一般會在下面的幾種情況中出現:
回呼
// `methodHandler()`中的`this`是全局对象 setTimeout(object.handlerMethod, 1000);
在設定事件處理程序時
// React: `methodHandler()`中的`this`是全局对象 <button onClick={object.handlerMethod}> Click me </button>
接著介紹一些有用的方法,即如果方法與物件分離,如何使this指向所需的物件。
2. 關閉上下文
保持this指向類別實例的最簡單方法是使用一個額外的變數self:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; const self = this; this.getFullName = function() { self === agent; // => true return `${self.firstName} ${self.lastName}`; } } const agent = new Person('前端', '小智'); agent.getFullName(); // => '前端 小智' execute(agent.getFullName); // => '前端 小智'
getFullName()靜態關閉self變量,有效地對this進行手動綁定。
現在,當呼叫execute(agent.getFullName)時,一切都運作正常,因為getFullName()方法內 this 總是指向正確的值。
3. 使用箭頭函數
有沒有辦法在沒有附加變數的情況下靜態綁定this?是的,這正是箭頭函數的作用。
使用箭頭函數重構Person:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = () => `${this.firstName} ${this.lastName}`; } const agent = new Person('前端', '小智'); agent.getFullName(); // => '前端 小智' execute(agent.getFullName); // => '前端 小智'
箭頭函數以詞法方式綁定this。簡單來說,它使用來自其定義的外部函數this的值。
建議在需要使用外部函數上下文的所有情況下都使用箭頭函數。
4. 綁定上下文
現在讓咱們更進一步,使用ES6中的類別重構Person。
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return `${this.firstName} ${this.lastName}`; } } const agent = new Person('前端', '小智'); agent.getFullName(); // => '前端 小智' execute(agent.getFullName); // => 'undefined undefined'
不幸的是,即使使用新的類別語法,execute(agent.getFullName)仍然傳回「undefined undefined」。
在類別的情況下,使用附加的變數self或箭頭函數來修復this的指向是行不通的。
但有一個涉及bind()方法的技巧,它將方法的上下文綁定到建構子:
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = this.getFullName.bind(this); } getFullName() { return `${this.firstName} ${this.lastName}`; } } const agent = new Person('前端', '小智'); agent.getFullName(); // => '前端 小智' execute(agent.getFullName); // => '前端 小智'
建構子中的this.getFullName = this.getFullName.bind( this)將方法getFullName()綁定到類別實例。
execute(agent.getFullName) 按預期工作,返回'前端 小智'。
5. 胖箭頭法
bind 方式有點太冗長,咱們可以使用胖箭頭的方式:
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName = () => { return `${this.firstName} ${this.lastName}`; } } const agent = new Person('前端', '小智'); agent.getFullName(); // => '前端 小智' execute(agent.getFullName); // => '前端 小智'
胖箭頭方法getFullName =() =>{…}綁定到類別實例,即使將方法與其物件分離。
這種方法是在類別中綁定this的最有效和最簡潔的方法。
6. 總結
與物件分離的方法會產生 this 指向不正確問題。靜態地綁定this,可以手動使用一個附加變數self來儲存正確的上下文物件。然而,更好的替代方法是使用箭頭函數,其本質上是為了在詞法上綁定this。
在類別中,可以使用bind()方法手動綁定建構子中的類別方法。當然如果你不用使用 bind 這種冗長方式,也可以使用簡潔方便的胖箭頭表示方法。
原文:https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter5.md
本文來自 js教學 欄目,歡迎學習!
以上是5種正確處理JS的this指向的方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!