亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

從這兩套題,重新認(rèn)識(shí)JS的this、作用域、閉包、對(duì)象

羅志環(huán) / 1848人閱讀

摘要:也就是說,普通情況下,指向調(diào)用函數(shù)時(shí)的對(duì)象。在全局執(zhí)行時(shí),則是全局對(duì)象。故而的方法因?yàn)闃?gòu)造函數(shù)閉包的關(guān)系,指向了構(gòu)造函數(shù)作用域內(nèi)的。

日常開發(fā)中,我們經(jīng)常用到this。例如用Jquery綁定事件時(shí),this指向觸發(fā)事件的DOM元素;編寫Vue、React組件時(shí),this指向組件本身。對(duì)于新手來說,常會(huì)用一種意會(huì)的感覺去判斷this的指向。以至于當(dāng)遇到復(fù)雜的函數(shù)調(diào)用時(shí),就分不清this的真正指向。

本文將通過兩道題去慢慢分析this的指向問題,并涉及到函數(shù)作用域與對(duì)象相關(guān)的點(diǎn)。最終給大家?guī)碚嬲睦碚摲治?,而不是簡簡單單的一句話概括?/p>

相信若是對(duì)this稍有研究的人,都會(huì)搜到這句話:this總是指向調(diào)用該函數(shù)的對(duì)象。

然而箭頭函數(shù)并不是如此,于是大家就會(huì)遇到如下各式說法:

箭頭函數(shù)的this指向外層函數(shù)作用域中的this。

箭頭函數(shù)的this是定義函數(shù)時(shí)所在上下文中的this。

箭頭函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。

各式各樣的說法都有,乍看下感覺說的差不多。廢話不多說,憑著你之前的理解,來先做一套題吧(非嚴(yán)格模式下)。

/**
 * Question 1
 */

var name = "window"

var person1 = {
  name: "person1",
  show1: function () {
    console.log(this.name)
  },
  show2: () => console.log(this.name),
  show3: function () {
    return function () {
      console.log(this.name)
    }
  },
  show4: function () {
    return () => console.log(this.name)
  }
}
var person2 = { name: "person2" }

person1.show1()
person1.show1.call(person2)

person1.show2()
person1.show2.call(person2)

person1.show3()()
person1.show3().call(person2)
person1.show3.call(person2)()

person1.show4()()
person1.show4().call(person2)
person1.show4.call(person2)()

大致意思就是,有兩個(gè)對(duì)象person1,person2,然后花式調(diào)用person1中的四個(gè)show方法,預(yù)測真正的輸出。

你可以先把自己預(yù)測的答案按順序記在本子上,然后再往下拉看正確答案。

正確答案選下:

person1.show1() // person1
person1.show1.call(person2) // person2

person1.show2() // window
person1.show2.call(person2) // window

person1.show3()() // window
person1.show3().call(person2) // person2
person1.show3.call(person2)() // window

person1.show4()() // person1
person1.show4().call(person2) // person1
person1.show4.call(person2)() // person2

對(duì)比下你剛剛記下的答案,是否有不一樣呢?讓我們嘗試來最開始那些理論來分析下。

person1.show1()person1.show1.call(person2)好理解,驗(yàn)證了誰調(diào)用此方法,this就是指向誰。

person1.show2()person1.show2.call(person2)的結(jié)果用上面的定義解釋,就開始讓人不理解了。

它的執(zhí)行結(jié)果說明this指向的是window。那就不是所謂的定義時(shí)所在的對(duì)象。

如果說是外層函數(shù)作用域中的this,實(shí)際上并沒有外層函數(shù)了,外層就是全局環(huán)境了,這個(gè)說法也不嚴(yán)謹(jǐn)。

只有定義函數(shù)時(shí)所在上下文中的this這句話算能描述現(xiàn)在這個(gè)情況。

person1.show3是一個(gè)高階函數(shù),它返回了一個(gè)函數(shù),分步走的話,應(yīng)該是這樣:

var func = person3.show()

func()

從而導(dǎo)致最終調(diào)用函數(shù)的執(zhí)行環(huán)境是window,但并不是window對(duì)象調(diào)用了它。所以說,this總是指向調(diào)用該函數(shù)的對(duì)象,這句話還得補(bǔ)充一句:在全局函數(shù)中,this等于window。

person1.show3().call(person2)person1.show3.call(person2)() 也好理解了。前者是通過person2調(diào)用了最終的打印方法。后者是先通過person2調(diào)用了person1的高階函數(shù),然后再在全局環(huán)境中執(zhí)行了該打印方法。

person1.show4()(),person1.show4().call(person2)都是打印person1。這好像又印證了那句:箭頭函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。因?yàn)榧词刮矣眠^person2去調(diào)用這個(gè)箭頭函數(shù),它指向的還是person1。

然而person1.show4.call(person2)()的結(jié)果又是person2。this值又發(fā)生改變,看來上述那句描述又走不通了。一步步來分析,先通過person2執(zhí)行了show4方法,此時(shí)show4第一層函數(shù)的this指向的是person2。所以箭頭函數(shù)輸出了person2的name。也就是說,箭頭函數(shù)的this指向的是誰調(diào)用箭頭函數(shù)的外層function,箭頭函數(shù)的this就是指向該對(duì)象,如果箭頭函數(shù)沒有外層函數(shù),則指向window。這樣去理解show2方法,也解釋的通。

這句話就對(duì)了么?在我們學(xué)習(xí)的過程中,我們總是想以總結(jié)規(guī)律的方法去總結(jié)結(jié)論,并且希望結(jié)論越簡單越容易描述就越好。實(shí)際上可能會(huì)錯(cuò)失真理。

下面我們再做另外一個(gè)相似的題目,通過構(gòu)造函數(shù)來創(chuàng)建一個(gè)對(duì)象,并執(zhí)行相同的4個(gè)show方法。

/**
 * Question 2
 */
var name = "window"

function Person (name) {
  this.name = name;
  this.show1 = function () {
    console.log(this.name)
  }
  this.show2 = () => console.log(this.name)
  this.show3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.show4 = function () {
    return () => console.log(this.name)
  }
}

var personA = new Person("personA")
var personB = new Person("personB")

personA.show1()
personA.show1.call(personB)

personA.show2()
personA.show2.call(personB)

personA.show3()()
personA.show3().call(personB)
personA.show3.call(personB)()

personA.show4()()
personA.show4().call(personB)
personA.show4.call(personB)()

同樣的,按照之前的理解,再次預(yù)計(jì)打印結(jié)果,把答案記下來,再往下拉看正確答案。

正確答案選下:

personA.show1() // personA
personA.show1.call(personB) // personB

personA.show2() // personA
personA.show2.call(personB) // personA

personA.show3()() // window
personA.show3().call(personB) // personB
personA.show3.call(personB)() // window

personA.show4()() // personA
personA.show4().call(personB) // personA
personA.show4.call(personB)() // personB

我們發(fā)現(xiàn)與之前字面量聲明的相比,show2方法的輸出產(chǎn)生了不一樣的結(jié)果。為什么呢?雖然說構(gòu)造方法Person是有自己的函數(shù)作用域。但是對(duì)于personA來說,它只是一個(gè)對(duì)象,在直觀感受上,它跟第一道題中的person1應(yīng)該是一模一樣的。 JSON.stringify(new Person("person1")) === JSON.stringify(person1)也證明了這一點(diǎn)。

說明構(gòu)造函數(shù)創(chuàng)建對(duì)象與直接用字面量的形式去創(chuàng)建對(duì)象,它是不同的,構(gòu)造函數(shù)創(chuàng)建對(duì)象,具體做了什么事呢?我引用紅寶書中的一段話。

使用 new 操作符調(diào)用構(gòu)造函數(shù),實(shí)際上會(huì)經(jīng)歷一下4個(gè)步驟:

創(chuàng)建一個(gè)新對(duì)象;

將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象);

執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性);

返回新對(duì)象。

所以與字面量創(chuàng)建對(duì)象相比,很大一個(gè)區(qū)別是它多了構(gòu)造函數(shù)的作用域。我們用chrome查看這兩者的作用域鏈就能清晰的知道:

personA的函數(shù)的作用域鏈從構(gòu)造函數(shù)產(chǎn)生的閉包開始,而person1的函數(shù)作用域僅是global,于是導(dǎo)致this指向的不同。我們發(fā)現(xiàn),要想真正理解this,先得知道到底什么是作用域,什么是閉包。

有簡單的說法稱閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。然而這是一種閉包現(xiàn)象的描述,而不是它的本質(zhì)與形成的原因。

我再次引用紅寶書的文字(便于理解,文字順序稍微調(diào)整),來描述這幾個(gè)點(diǎn):

...每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境(execution context,也叫執(zhí)行上下文),每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中。

...當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí),函數(shù)的環(huán)境就會(huì)被推入一個(gè)環(huán)境棧中。當(dāng)代碼在環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建一個(gè)作用域鏈,來保證對(duì)執(zhí)行環(huán)境中的所有變量和函數(shù)的有序訪問。函數(shù)執(zhí)行之后,棧將環(huán)境彈出。

...函數(shù)內(nèi)部定義的函數(shù)會(huì)將包含函數(shù)的活動(dòng)對(duì)象添加到它的作用域鏈中。

具體來說,當(dāng)我們 var func = personA.show3() 時(shí),personAshow3函數(shù)的活動(dòng)對(duì)象,會(huì)一直保存在func的作用域鏈中。只要不銷毀func,那么show3函數(shù)的活動(dòng)對(duì)象就會(huì)一直保存在內(nèi)存中。(chrome的v8引擎對(duì)閉包的開銷會(huì)有優(yōu)化)

而構(gòu)造函數(shù)同樣也是閉包的機(jī)制,personAshow1方法,是構(gòu)造函數(shù)的內(nèi)部函數(shù),因此執(zhí)行了 this.show1 = function () { console.log(this.name) }時(shí),已經(jīng)把構(gòu)造函數(shù)的活動(dòng)對(duì)象推到了show1函數(shù)的作用域鏈中。

我們再回到this的指向問題。我們發(fā)現(xiàn),單單是總結(jié)規(guī)律,或者用一句話概括,已經(jīng)難以正確解釋它到底指向誰了,我們得追本溯源。

紅寶書中說道:

...this引用的是函數(shù)執(zhí)行的環(huán)境對(duì)象(便于理解,貼上英文原版:It is a reference to the context object that the function is operating on)。
...每個(gè)函數(shù)被調(diào)用時(shí)都會(huì)自動(dòng)獲取兩個(gè)特殊變量:this和arguments。內(nèi)部在搜索這個(gè)兩個(gè)變量時(shí),只會(huì)搜索到其活動(dòng)對(duì)象為止,永遠(yuǎn)不可能直接訪問外部函數(shù)中的這兩個(gè)變量。

我們看下MDN中箭頭函數(shù)的概念:

一個(gè)箭頭函數(shù)表達(dá)式的語法比一個(gè)函數(shù)表達(dá)式更短,并且不綁定自己的 thisarguments,supernew.target。...箭頭函數(shù)會(huì)捕獲其所在上下文的 this 值,作為自己的 this 值。

也就是說,普通情況下,this指向調(diào)用函數(shù)時(shí)的對(duì)象。在全局執(zhí)行時(shí),則是全局對(duì)象。

箭頭函數(shù)的this,因?yàn)闆]有自身的this,所以this只能根據(jù)作用域鏈往上層查找,直到找到一個(gè)綁定了this的函數(shù)作用域(即最靠近箭頭函數(shù)的普通函數(shù)作用域,或者全局環(huán)境),并指向調(diào)用該普通函數(shù)的對(duì)象。

或者從現(xiàn)象來描述的話,即箭頭函數(shù)的this指向聲明函數(shù)時(shí),最靠近箭頭函數(shù)的普通函數(shù)的this。但這個(gè)this也會(huì)因?yàn)檎{(diào)用該普通函數(shù)時(shí)環(huán)境的不同而發(fā)生變化。導(dǎo)致這個(gè)現(xiàn)象的原因是這個(gè)普通函數(shù)會(huì)產(chǎn)生一個(gè)閉包,將它的變量對(duì)象保存在箭頭函數(shù)的作用域中。

故而personAshow2方法因?yàn)闃?gòu)造函數(shù)閉包的關(guān)系,指向了構(gòu)造函數(shù)作用域內(nèi)的this。而

var func = personA.show4.call(personB)

func() // print personB

因?yàn)閜ersonB調(diào)用了personA的show4,使得返回函數(shù)func的作用域的this綁定為personB,進(jìn)而調(diào)用func時(shí),箭頭函數(shù)通過作用域找到的第一個(gè)明確的this為personB。進(jìn)而輸出personB。

講了這么多,可能還是有點(diǎn)繞??傊?,想充分理解this的前提,必須得先明白js的執(zhí)行環(huán)境、閉包、作用域、構(gòu)造函數(shù)等基礎(chǔ)知識(shí)。然后才能得出清晰的結(jié)論。

我們平常在學(xué)習(xí)過程中,難免會(huì)更傾向于根據(jù)經(jīng)驗(yàn)去推導(dǎo)結(jié)論,或者直接去找一些通俗易懂的描述性語句。然而實(shí)際上可能并不是最正確的結(jié)果。如果想真正掌握它,我們就應(yīng)該追本溯源的去研究它的內(nèi)部機(jī)制。

我上述所說也是我自己推導(dǎo)出的結(jié)果,即使它不一定正確,但這個(gè)推斷思路跟學(xué)習(xí)過程,我覺得可以跟大家分享分享。

--閱讀原文 @相學(xué)長

--轉(zhuǎn)載請先經(jīng)過本人授權(quán)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/85170.html

相關(guān)文章

  • 【進(jìn)階3-2期】JavaScript深入之重新認(rèn)識(shí)箭頭函數(shù)this

    摘要:箭頭函數(shù)的尋值行為與普通變量相同,在作用域中逐級(jí)尋找。題目這次通過構(gòu)造函數(shù)來創(chuàng)建一個(gè)對(duì)象,并執(zhí)行相同的個(gè)方法。 我們知道this綁定規(guī)則一共有5種情況: 1、默認(rèn)綁定(嚴(yán)格/非嚴(yán)格模式) 2、隱式綁定 3、顯式綁定 4、new綁定 5、箭頭函數(shù)綁定 其實(shí)大部分情況下可以用一句話來概括,this總是指向調(diào)用該函數(shù)的對(duì)象。 但是對(duì)于箭頭函數(shù)并不是這樣,是根據(jù)外層(函數(shù)或者全局)作用域(...

    Rainie 評(píng)論0 收藏0
  • JavaScript之例題中徹底理解this

    摘要:最后重點(diǎn)理解結(jié)論箭頭函數(shù)的,總是指向定義時(shí)所在的對(duì)象,而不是運(yùn)行時(shí)所在的對(duì)象。輸出,箭頭函數(shù)不會(huì)綁定所以傳入指向無效。原因是,要徹底理解應(yīng)該是建立在已經(jīng)大致理解了中的執(zhí)行上下文,作用域作用域鏈,閉包,變量對(duì)象,函數(shù)執(zhí)行過程的基礎(chǔ)上。 本文共 2025 字,看完只需 8 分鐘 概述 前面的文章講解了 JavaScript 中的執(zhí)行上下文,作用域,變量對(duì)象,this 的相關(guān)原理,但是我...

    Hwg 評(píng)論0 收藏0
  • 前端經(jīng)典文章

    摘要:上周末看這篇文章時(shí),偶有靈光,所以,分享出來給大家一起看看前端面試四月二十家前端面試題分享請各位讀者添加一下作者的微信公眾號(hào),以后有新的文章,將在微信公眾號(hào)直接推送給各位,非常感謝。 前端切圖神器 avocode 有了這個(gè)神器,切圖再也腰不酸,腿不疼了。 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,...

    lowett 評(píng)論0 收藏0
  • JavasScript重難點(diǎn)知識(shí)

    摘要:忍者級(jí)別的函數(shù)操作對(duì)于什么是匿名函數(shù),這里就不做過多介紹了。我們需要知道的是,對(duì)于而言,匿名函數(shù)是一個(gè)很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個(gè)供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...

    forsigner 評(píng)論0 收藏0
  • 面試官問:JSthis指向

    摘要:之前寫過一篇文章面試官問能否模擬實(shí)現(xiàn)的和方法就是利用對(duì)象上的函數(shù)指向這個(gè)對(duì)象,來模擬實(shí)現(xiàn)和的。雖然實(shí)際使用時(shí)不會(huì)顯示返回,但面試官會(huì)問到。非嚴(yán)格模式下,和,指向全局對(duì)象 前言 面試官出很多考題,基本都會(huì)變著方式來考察this指向,看候選人對(duì)JS基礎(chǔ)知識(shí)是否扎實(shí)。讀者可以先拉到底部看總結(jié),再谷歌(或各技術(shù)平臺(tái))搜索幾篇類似文章,看筆者寫的文章和別人有什么不同(歡迎在評(píng)論區(qū)評(píng)論不同之處),...

    warnerwu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<