做者:Dmitri Pavlutinhtml
來源:dmitripavlutin前端
譯者:前端小智git
阿里雲最近在作活動,低至2折,有興趣能夠看看:promotion.aliyun.com/ntms/yunpar…github
爲了保證的可讀性,本文采用意譯而非直譯。數組
我喜歡在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
綁定到所需的值。學習
在開始以前,我須要一個輔助函數execute(func)
,它僅執行做爲參數提供的函數。this
function execute(func) {
return func();
}
execute(function() { return 10 }); // => 10
複製代碼
如今,繼續理解圍繞this
錯誤的本質:方法分離。
假設有一個類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
與原始對象沒有鏈接。
爲了確保方法內部的this
指向正確的對象,必須這樣作
agent.getFullName()
this
綁定到包含的對象(使用箭頭函數、.bind()
方法等)方法分離問題,以及由此致使this
指向不正確,通常會在下面的幾種狀況中出現:
回調
// `methodHandler()`中的`this`是全局對象
setTimeout(object.handlerMethod, 1000);
複製代碼
在設置事件處理程序時
// React: `methodHandler()`中的`this`是全局對象
<button onClick={object.handlerMethod}>
Click me
</button>
複製代碼
接着介紹一些有用的方法,即若是方法與對象分離,如何使this
指向所需的對象。
保持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
老是指向正確的值。
有沒有辦法在沒有附加變量的狀況下靜態綁定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
的值。
建議在須要使用外部函數上下文的全部狀況下都使用箭頭函數。
如今讓我們更進一步,使用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)
按預期工做,返回'前端 小智'
。
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
的最有效和最簡潔的方法。
與對象分離的方法會產生 this 指向不正確問題。靜態地綁定this
,能夠手動使用一個附加變量self
來保存正確的上下文對象。然而,更好的替代方法是使用箭頭函數,其本質上是爲了在詞法上綁定this
。
在類中,可使用bind()
方法手動綁定構造函數中的類方法。固然若是你不用使用 bind
這種冗長方式,也可使用簡潔方便的胖箭頭表示方法。
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
阿里雲最近在作活動,低至2折,有興趣能夠看看:promotion.aliyun.com/ntms/yunpar…
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。