this的指向已是一個老生常談的問題,每逢面試都要去複習複習,近來鞏固js的基礎,決心完全掌握這個知識點,一勞永逸。說明一下,爲了避免影響你們的思考過程,下面的代碼都不會去註釋答案,想知道答案,只須要去控制檯執行一下。面試
首先,分析this的指向共有四種類型,在分析以前,咱們首先帶好兩個錦囊:
1.函數被調用時(即運行時)纔會肯定該函數內this的指向。由於在函數中this與arguments是兩個特殊的變量,在函數被調用時纔會取得它們,並且搜索這兩個變量時只會在活動對象範圍裏面去搜。(有關活動對象與變量對象的知識,請移步到js 中的活動對象 與 變量對象 什麼區別?)
2.要肯定函數中this的指向,必須先找到該函數被調用的位置。瀏覽器
var a = 1 function test () { console.log(this.a) } test()
直接不帶任何引用形式去調用函數,則this會指向全局對象,由於沒有其餘影響去改變this,this默認就是指向全局對象(瀏覽器是window,Node中是global)的。這個結論是在非嚴格模式的狀況下,嚴格模式下這個this實際上是undefined的。安全
var a = 1 function test () { console.log(this.a) } var obj = { a: 2, test } obj.test()
這種形式對比起第一種,很明顯test()已是名花有主的了!看清楚,是誰呼喚的test()?沒錯,就是obj,因此this的指向就不言而喻了。一句話,誰去調用這個函數的,這個函數中的this就綁定到誰身上。app
var a = 1 function test () { console.log(this.a) } var obj = { a: 2, test } var obj0 = { a: 3, obj } obj0.obj.test()
即便是這種串串燒的形式,結果也是同樣的,test()中的this只對直屬上司(直接調用者obj)負責。再來看一個綜合點的例子:函數
var a = 1 function test () { console.log(this.a) } var obj = { a: 2, test } var testCopy = obj.test testCopy()
嗯,聰明的你必定想到,換了個名字就能騙到我了!?雖然通過了一波更名換姓,但本質上還不是obj.test()嘛!結果必定和上面同樣!唔,請F12在控制檯試試,居然……其實這裏並不須要去思考什麼,按照咱們的套路,咱們就認函數調時的樣子,有沒有看到最後調用的時候跟第一種狀況一毛同樣?我再介紹一個場景你們必定不會以爲陌生:this
var a = 1 function test () { console.log(this.a) } var obj = { a: 2, test } setTimeout(obj.test)
你能夠意淫一下setTimeout的本質,是否是至關於有一個setTimeout函數,接收兩個參數:code
function setTimeout (fn, time) { // 這裏幹了一大波不可描述的事情,最後會去調一下你傳進來的回調函數 fn() }
看到怎樣調用你傳進來的函數了嗎!?再想一想咱們第一種形式的標題認準第一種「test()」形式。對象
看了上面兩種形式以後,你可能會想,我很是討厭上面那些矯情的扭扭捏捏的九曲十八彎的調用方式,讓人毫無安全感,我要我本身指定this的指向,我要勝天半子!沒問題,個人代碼我作主:作用域
var a = 1 function test () { console.log(this.a) } var obj = { a: 2, test } var testCopy = obj.test testCopy.call(obj)
能夠看到,咱們經過call(apply跟call的區別只是傳參,做用是同樣的,bind有點區別,bind能讓咱們的函數延遲執行,apply與call調用就執行,因此bind這樣的形式咱們也稱爲函數柯里化,這些就不是咱們這裏要說的啦)來調用testCopy,而且傳入了你想要this指向的上下文,那麼this就會乖乖按照你的指示行事啦。看到這裏,咱們也能夠想象第1、二種形式其實能夠轉化成call/apply的形式,有一篇比較棒的文章描述了這樣的思考過程,你們也能夠看看this 的值究竟是什麼?一次說清楚get
終於到了最後一種形式了,這種形式比較好認,由於有標誌性的new:
var a = 1 function test (a) { this.a = a } var b = new test(2) console.log(b.a)
new這個操做符實際上是new了一個新對象出來,而被new的test咱們稱爲構造函數,咱們能夠在這個構造函數裏定義一下將要到來的新對象的一些屬性。那麼在構造函數裏,咱們怎樣去描述這個還未出生的新對象呢?沒錯,就是用this。因此構造函數裏的this指的就是將要被new出來的新對象。
感謝你們看到這裏,但還要說最後一種形式。等等,不是說好的只有四種形式嗎!稍安勿躁,正常套路下確實只有上面四種,可是有個東西別忘了,就是你們最喜歡的箭頭函數。
var a = 1 var test = () => { console.log(this.a) } var obj = { a: 2, test } obj.test()
來,往上翻一下咱們的第一個錦囊,「函數被調用時(即運行時)纔會肯定該函數內this的指向。」如今函數這兩個字要加個詞修飾一下,變成普通函數(非箭頭函數)才能區別於箭頭函數。箭頭函數中的this在函數定義的時候就已經肯定,它this指向的是它的外層做用域this的指向。
咱們最後還要說:「到此爲止,真的沒有了。」但願看完這篇文章以後,再有人問this指向的問題,你能夠嘴角微微上揚,冷笑一聲:「不要再問我this的指向問題了。」揚長而去。