淺談JS中幾種輕松處理'this'指向方式
我喜歡在JS中更改函數(shù)執(zhí)行上下文的指向,也稱為 this 指向。
例如,咱們可以在類數(shù)組對(duì)象上使用數(shù)組方法:
const reduce = Array.prototype.reduce; function sumArgs() { return reduce.call(arguments, (sum, value) => { return sum += value; }); } sumArgs(1, 2, 3); // => 6
另一方面,this 很難把握。
咱們經(jīng)常會(huì)發(fā)現(xiàn)自己用的 this 指向不正確。下面的教你如何簡(jiǎn)單地將 this 綁定到所需的值。
在開(kāi)始之前,我需要一個(gè)輔助函數(shù)execute(func),它僅執(zhí)行作為參數(shù)提供的函數(shù)。
function execute(func) { return func(); } execute(function() { return 10 }); // => 10
現(xiàn)在,繼續(xù)理解圍繞this錯(cuò)誤的本質(zhì):方法分離。
1.方法分離問(wèn)題
假設(shè)有一個(gè)類Person包含字段firstName和lastName。此外,它還有一個(gè)方法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函數(shù)作為構(gòu)造函數(shù)被調(diào)用:new Person('前端', '小智')。 函數(shù)內(nèi)部的 this 表示新創(chuàng)建的實(shí)例。
getfullname()返回此人的全名:'前端 小智'。正如預(yù)期的那樣,getFullName()方法內(nèi)的 this 等于agent。
如果輔助函數(shù)執(zhí)行agent.getFullName方法會(huì)發(fā)生什么:
execute(agent.getFullName); // => 'undefined undefined'
執(zhí)行結(jié)果不正確:'undefined undefined',這是 this 指向不正確導(dǎo)致的問(wèn)題。
現(xiàn)在在getFullName() 方法中,this的值是全局對(duì)象(瀏覽器環(huán)境中的 window )。 this 等于 window,${window.firstName} ${window.lastName} 執(zhí)行結(jié)果是 'undefined undefined'。
發(fā)生這種情況是因?yàn)樵谡{(diào)用execute(agent.getFullName)時(shí)該方法與對(duì)象分離。 基本上發(fā)生的只是常規(guī)函數(shù)調(diào)用(不是方法調(diào)用):
execute(agent.getFullName); // => 'undefined undefined' // 等價(jià)于: const getFullNameSeparated = agent.getFullName; execute(getFullNameSeparated); // => 'undefined undefined'
這個(gè)就是所謂的方法從它的對(duì)象中分離出來(lái),當(dāng)方法被分離,然后執(zhí)行時(shí),this 與原始對(duì)象沒(méi)有連接。
為了確保方法內(nèi)部的this指向正確的對(duì)象,必須這樣做
- 以屬性訪問(wèn)器的形式執(zhí)行方法:agent.getFullName()
- 或者靜態(tài)地將this綁定到包含的對(duì)象(使用箭頭函數(shù)、.bind()方法等)
方法分離問(wèn)題,以及由此導(dǎo)致this指向不正確,一般會(huì)在下面的幾種情況中出現(xiàn):
回調(diào)
// `methodHandler()`中的`this`是全局對(duì)象 setTimeout(object.handlerMethod, 1000);
在設(shè)置事件處理程序時(shí)
// React: `methodHandler()`中的`this`是全局對(duì)象 <button onClick={object.handlerMethod}> Click me </button>
接著介紹一些有用的方法,即如果方法與對(duì)象分離,如何使this指向所需的對(duì)象。
2. 關(guān)閉上下文
保持this指向類實(shí)例的最簡(jiǎn)單方法是使用一個(gè)額外的變量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()靜態(tài)地關(guān)閉self變量,有效地對(duì)this進(jìn)行手動(dòng)綁定。
現(xiàn)在,當(dāng)調(diào)用execute(agent.getFullName)時(shí),一切工作正常,因?yàn)間etFullName()方法內(nèi) this 總是指向正確的值。
3.使用箭頭函數(shù)
有沒(méi)有辦法在沒(méi)有附加變量的情況下靜態(tài)綁定this? 是的,這正是箭頭函數(shù)的作用。
使用箭頭函數(shù)重構(gòu)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); // => '前端 小智'
箭頭函數(shù)以詞法方式綁定this。 簡(jiǎn)單來(lái)說(shuō),它使用來(lái)自其定義的外部函數(shù)this的值。
建議在需要使用外部函數(shù)上下文的所有情況下都使用箭頭函數(shù)。
4. 綁定上下文
現(xiàn)在讓咱們更進(jìn)一步,使用ES6中的類重構(gòu)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'
不幸的是,即使使用新的類語(yǔ)法,execute(agent.getFullName)仍然返回“undefined undefined”。
在類的情況下,使用附加的變量self或箭頭函數(shù)來(lái)修復(fù)this的指向是行不通的。
但是有一個(gè)涉及bind()方法的技巧,它將方法的上下文綁定到構(gòu)造函數(shù)中:
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); // => '前端 小智'
構(gòu)造函數(shù)中的this.getFullName = this.getFullName.bind(this)將方法getFullName()綁定到類實(shí)例。
execute(agent.getFullName) 按預(yù)期工作,返回'前端 小智'。
5. 胖箭頭方法
bind 方式有點(diǎn)太過(guò)冗長(zhǎng),咱們可以使用胖箭頭的方式:
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 =() =>{…}綁定到類實(shí)例,即使將方法與其對(duì)象分離。
這種方法是在類中綁定this的最有效和最簡(jiǎn)潔的方法。
6. 總結(jié)
與對(duì)象分離的方法會(huì)產(chǎn)生 this 指向不正確問(wèn)題。靜態(tài)地綁定this,可以手動(dòng)使用一個(gè)附加變量self來(lái)保存正確的上下文對(duì)象。然而,更好的替代方法是使用箭頭函數(shù),其本質(zhì)上是為了在詞法上綁定this。
在類中,可以使用bind()方法手動(dòng)綁定構(gòu)造函數(shù)中的類方法。當(dāng)然如果你不用使用 bind 這種冗長(zhǎng)方式,也可以使用簡(jiǎn)潔方便的胖箭頭表示方法。
原文:https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter5.md
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Array 重排序方法和操作方法的簡(jiǎn)單實(shí)例
下面小編就為大家簡(jiǎn)單的介紹Array中reverse(),sort(),concat(),slice(),splice()方法的應(yīng)用。一起過(guò)來(lái)看看吧2014-01-01js利用事件的阻止冒泡實(shí)現(xiàn)點(diǎn)擊空白模態(tài)框的隱藏
點(diǎn)擊彈出某個(gè)框框,但是,有時(shí)候不想操作,就想點(diǎn)擊某個(gè)空白處,隱藏該框框,關(guān)于這一點(diǎn),下面有個(gè)具體的實(shí)現(xiàn)2014-01-01JS前端開(kāi)發(fā)模擬虛擬dom轉(zhuǎn)真實(shí)dom詳解
這篇文章主要為大家介紹了JS前端開(kāi)發(fā)模擬虛擬dom轉(zhuǎn)真實(shí)dom詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01JavaScript關(guān)閉當(dāng)前頁(yè)面(窗口)不帶任何提示
這篇文章主要介紹了JavaScript關(guān)閉當(dāng)前頁(yè)面(窗口)不帶任何提示的具體實(shí)現(xiàn),需要的朋友可以參考下2014-03-03JS+CSS3實(shí)現(xiàn)的簡(jiǎn)易鐘表效果示例
這篇文章主要介紹了JS+CSS3實(shí)現(xiàn)的簡(jiǎn)易鐘表效果,涉及JavaScript結(jié)合定時(shí)器的頁(yè)面元素動(dòng)態(tài)設(shè)置與數(shù)值計(jì)算相關(guān)操作技巧,需要的朋友可以參考下2019-04-04