我發(fā)現(xiàn)了華點(diǎn):vue規(guī)定用普通函數(shù)定義方法卤档,為什么react又要我用箭頭函數(shù)!

原文首發(fā)在我的公眾號(hào)寥粹,訂閱可第一時(shí)間查看我的最新文章!

大家好埃元,我是年年涝涤!

如果使用過(guò)react和vue,應(yīng)該發(fā)現(xiàn)過(guò)一個(gè)問題:vue告訴我們不應(yīng)該把方法岛杀、生命周期用箭頭函數(shù)去定義阔拳;而在react的類組件中,把方法寫成箭頭函數(shù)的形式卻更方便类嗤。

要問其原因糊肠,大部分人都只把他當(dāng)一個(gè)理所當(dāng)然的規(guī)定。但把這個(gè)問題剖開遗锣,其實(shí)能很好地把準(zhǔn)備面試時(shí)造的火箭货裹,在擰螺絲的時(shí)候用起來(lái)。

這篇文章可以讓你在這個(gè)實(shí)際場(chǎng)景中去用到this的指向精偿、作用域鏈以及原型弧圆。

this指向丟失

無(wú)論是vue還是react,都在官方文檔中強(qiáng)調(diào)笔咽,需要注意this的指向丟失搔预。但有趣的是,為了達(dá)到同樣的目的叶组,一個(gè)是不能使用箭頭函數(shù)拯田,一個(gè)是使用箭頭函數(shù)便能解決

React中this的丟失

首先來(lái)看看react,這是一個(gè)很普通的類組件寫法:

class Demo extends React.Component{
    state = {
        someState:'state'
    }
    // ?推薦
    arrowFunMethod = () => {
        console.log('THIS in arrow function:',this)
        this.setState({someState:'arrow state'})
    }
    // ?需要處理this綁定
    ordinaryFunMethod(){
        console.log('THIS oridinary function:',this)
        this.setState({someState:'ordinary state'})
    }
    render(){
        return ( 
            <div>
                <h2>{this.state.someState}</h2>
                <button onClick={this.arrowFunMethod}>call arrow function</button>
                <button onClick={this.ordinaryFunMethod}>call ordinary function</button>
            </div>
        )
    }
}
ReactDOM.render(<Demo/>,document.getElementById('root'))

我在組件內(nèi)我定義了兩個(gè)方法:一個(gè)用箭頭函數(shù)實(shí)現(xiàn)扶叉,另一個(gè)用普通函數(shù)勿锅。在調(diào)用時(shí)分別打印this,結(jié)果如下:

[圖片上傳失敗...(image-db8f01-1649345218334)]

箭頭函數(shù)中this正確指向了組件實(shí)例枣氧,但普通函數(shù)中卻指向了undefined溢十,為什么?

其實(shí)這是一個(gè)無(wú)關(guān)react的js特性达吞,剝離react帶來(lái)的心智負(fù)擔(dān)张弛,本質(zhì)上,上面的代碼不過(guò)是一個(gè)「類」,簡(jiǎn)化一下吞鸭,就變成了這樣??:

 class ReactDemo {
    // ?推薦
    arrowFunMethod = () => {
      console.log('THIS in arrow function:', this)
    }
    // ?this指向丟失
    ordinaryFunMethod() {
      console.log('THIS in oridinary function:', this)
    }
  }
  const reactIns = new ReactDemo()
  let arrowFunWithoutCaller = reactIns.arrowFunMethod
  let ordinaryFunWithoutCaller = reactIns.ordinaryFunMethod
  arrowFunWithoutCaller()
  ordinaryFunWithoutCaller()

運(yùn)行一下上面這段代碼寺董,會(huì)發(fā)現(xiàn)結(jié)果不出預(yù)料:在普通函數(shù)中this的指向也丟失了。

[圖片上傳失敗...(image-ca5172-1649345218334)]

從react代碼運(yùn)行的角度來(lái)解釋一下:

首先是事件觸發(fā)時(shí)刻剥,回調(diào)函數(shù)的執(zhí)行遮咖。回調(diào)函數(shù)不是像這樣直接由實(shí)例調(diào)用:reactIns.ordinaryFunMethod()造虏,而是像上面代碼中的御吞,做了一次“代理”,最后被調(diào)用時(shí)漓藕,找不到調(diào)用對(duì)象了:ordinaryFunWithoutCaller()陶珠。這時(shí)就出現(xiàn)了this指向undefined的情況。

但為什么使用箭頭函數(shù)享钞,this又可以正確指向組件實(shí)例呢揍诽?首先回顧一個(gè)簡(jiǎn)單的知識(shí)點(diǎn):class是個(gè)語(yǔ)法糖,本質(zhì)不過(guò)是個(gè)構(gòu)造函數(shù)栗竖,把上面的代碼用它最原始的樣子寫出來(lái):

'use strict'
function ReactDemo() {
  // ?推薦
  this.arrowFunMethod = () => {
    console.log('THIS in arrow function:', this)
  }
}
// ?this指向丟失
ReactDemo.prototype.ordinaryFunMethod = function ordinaryFunMethod() {
  console.log('THIS in oridinary function:', this)
}
const reactIns = new ReactDemo()

可以看到:寫成普通函數(shù)的方法暑脆,是被掛載到原型鏈上的;而使用箭頭函數(shù)定義的方法划滋,直接賦給了實(shí)例饵筑,變成了實(shí)例的一個(gè)屬性,并且最重要的是:它是在「構(gòu)造函數(shù)的作用域」被定義的处坪。

我們知道根资,箭頭函數(shù)沒有自己的this,用到的時(shí)候只能根據(jù)作用域鏈去尋找最近的那個(gè)同窘。放在這里玄帕,也就是構(gòu)造函數(shù)這個(gè)作用域中的this——組件實(shí)例。

這樣就可以解釋為什么react組件中想邦,箭頭函數(shù)的this能正確指向組件實(shí)例裤纹。

vue中this的丟失

把上面的組件用vue來(lái)寫一遍:

const Demo = Vue.createApp({
  data() {
      return {
          someState:'state',
      }
  },
  methods:{
      // ?this指向丟失
      arrowFunMethod:()=>{
          console.log('THIS in arrow function:',this)
          this.someState = 'arrow state'
      },
      // ?推薦
      ordinaryFunMethod(){
          console.log('THIS in oridinary function:',this)
          this.someState = 'ordinary state'
      }
  },
  template:`
  <div>
      <h2>{{this.someState}}</h2>
      <button @click='this.arrowFunMethod'>call arrow function</button>
      <button @click='this.ordinaryFunMethod'>call ordinary function</button>
  </div>`
})
Demo.mount('#root')

運(yùn)行代碼,會(huì)發(fā)現(xiàn)結(jié)果對(duì)調(diào)了:使用箭頭函數(shù)反而導(dǎo)致了this指向丟失:this指向了window對(duì)象
[圖片上傳失敗...(image-ff05e3-1649345218334)]

這部分解釋起來(lái)會(huì)稍微復(fù)雜一下丧没,不過(guò)也只涉及一小塊vue源碼鹰椒。主要的操作是vue對(duì)組件方法的處理,最核心的就三行呕童,感興趣的可以去看看完整代碼:vue-github

function initMethods(vm: Component, methods: Object) {
  for (const key in methods) {
    vm[key] = bind(methods[key], vm)
  }
}

vue會(huì)把我們傳入methods遍歷漆际,再一個(gè)個(gè)賦給到組建實(shí)例上,在這個(gè)過(guò)程就處理了this的綁定(bind(methods[key], vm)):把每一個(gè)方法中的this都綁定到組件實(shí)例上夺饲。

普通函數(shù)都有自己的this奸汇,所以綁定完后施符,被調(diào)用時(shí)都能正確指向組件實(shí)例。但箭頭函數(shù)沒有自己的this擂找,便無(wú)從談及修改戳吝,它只能去找父級(jí)作用域中的this。這個(gè)父級(jí)作用域是誰(shuí)呢贯涎?是組件實(shí)例嗎听哭?我們知道作用域只有兩種:全局作用域和函數(shù)作用域〖聿桑回到我們寫的vue代碼欢唾,它本質(zhì)就是一個(gè)對(duì)象(具體一點(diǎn),是一個(gè)組件的配置對(duì)象粉捻,這個(gè)對(duì)象里面有data、mounted斑芜、methods等屬性)也就是說(shuō)肩刃,我們?cè)谝粋€(gè)對(duì)象里面去定義方法,因?yàn)閷?duì)象不構(gòu)成作用域杏头,所以這些方法的父作用域都是全局作用域盈包。箭頭函數(shù)要去尋找this,就只能找到全局作用域中的this——window對(duì)象了醇王。

上面說(shuō)了這么多呢燥,總結(jié)一下:vue對(duì)傳入的方法methods對(duì)象做了處理,在函數(shù)被調(diào)用前做了this指向的綁定寓娩,只有擁有this的普通函數(shù)才能被正確的綁定到組件實(shí)例上叛氨。而箭頭函數(shù)則會(huì)導(dǎo)致this的指向丟失。

結(jié)語(yǔ)

「為什么react中用箭頭函數(shù)棘伴,vue中用普通函數(shù)」這是一個(gè)挺很有意思的問題寞埠,簡(jiǎn)單來(lái)說(shuō),這種差異是由于我們寫的react是一個(gè)類焊夸,而vue是一個(gè)對(duì)象導(dǎo)致的仁连。

在類中定義只有箭頭函數(shù)才能根據(jù)作用域鏈找到組件實(shí)例;在對(duì)象中阱穗,只有擁有自身this的普通函數(shù)才能被修改this指向饭冬,被vue處理后綁定到組件實(shí)例。

如果覺得這篇文章對(duì)你有幫助揪阶,不要忘了給我點(diǎn)個(gè)贊昌抠,你的支持是我最大的動(dòng)力??????

關(guān)注我的公眾號(hào)可以第一時(shí)間看到最新文章??????


IMG_6474.JPG

點(diǎn)個(gè)在看更好??????

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市遣钳,隨后出現(xiàn)的幾起案子扰魂,更是在濱河造成了極大的恐慌麦乞,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劝评,死亡現(xiàn)場(chǎng)離奇詭異姐直,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蒋畜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門声畏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人姻成,你說(shuō)我怎么就攤上這事插龄。” “怎么了科展?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵均牢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我才睹,道長(zhǎng)徘跪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任琅攘,我火速辦了婚禮垮庐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坞琴。我一直安慰自己哨查,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布剧辐。 她就那樣靜靜地躺著寒亥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浙于。 梳的紋絲不亂的頭發(fā)上护盈,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音羞酗,去河邊找鬼腐宋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛檀轨,可吹牛的內(nèi)容都是我干的胸竞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼参萄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼卫枝!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起讹挎,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤校赤,失蹤者是張志新(化名)和其女友劉穎吆玖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體马篮,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沾乘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浑测。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翅阵。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖迁央,靈堂內(nèi)的尸體忽然破棺而出掷匠,到底是詐尸還是另有隱情,我是刑警寧澤岖圈,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布讹语,位于F島的核電站,受9級(jí)特大地震影響蜂科,放射性物質(zhì)發(fā)生泄漏募强。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一崇摄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧慌烧,春花似錦逐抑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至汹粤,卻和暖如春命斧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嘱兼。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工国葬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芹壕。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓汇四,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親踢涌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子通孽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容