
少俠們好~
一段時(shí)間沒見了,
看見標(biāo)題,你應(yīng)該知道了這次我們的主要內(nèi)容是什么。
而且,你或許會(huì)以為我是一個(gè)標(biāo)題黨,
點(diǎn)進(jìn)來是想吐槽我,
但這次我要告訴你,
不是。
我確實(shí)認(rèn)為這會(huì)是目前為止關(guān)于this最好的文章。
為什么我要這么說?
因?yàn)槊灾孕牛?/p>
關(guān)于this關(guān)鍵字的文章已經(jīng)有很多了,少俠你可能也看過不少了,
那么少俠請(qǐng)讓我先來歸個(gè)類,然后你用最近看的文章對(duì)號(hào)入座試試,
不出意外,大概應(yīng)該有這么幾種類型的文章:
1、分門別類細(xì)致耐心型
這種類型的特點(diǎn)就是,會(huì)比較詳細(xì)的給你列出各種可能遇見this的情況,告訴你每種情況的答案,
比如:
“作為函數(shù)調(diào)用。。。balabala”
“作為對(duì)象方法調(diào)用。。。balabala”
“new關(guān)鍵字調(diào)用。。。balabala”
。。。。
當(dāng)然,
如果列出的例子夠詳細(xì),各種this的情況可能也不太能難住你。
一個(gè)廣場舞大媽曾告訴我,如果她跳的足夠快,她的孤獨(dú)就追不上她;一位拾荒大叔曾經(jīng)告訴我,如果他翻垃圾翻得足夠仔細(xì),便能找回丟失的自己;一位環(huán)衛(wèi)工阿姨曾經(jīng)告訴我,她每天都掃這兩條街,七年了,都沒掃干凈心中的瑕疵;一位碰瓷的大爺曾經(jīng)告訴我,只要他演的夠逼真,就能騙過匆匆流逝的時(shí)光.....
2、簡單粗暴不想廢話型
和第一種類型相反,這種類型的特點(diǎn)是一般會(huì)有一個(gè)比較簡短的總結(jié),然后通常讓你記住這種特點(diǎn)就行了,不想過多糾結(jié)各種例子。
比如:
“this 永遠(yuǎn)指向最后調(diào)用它的那個(gè)對(duì)象。。。。。。balabala”
“this是你調(diào)用一個(gè)函數(shù)時(shí)的上下文context。。。balabala”
“。。?!?/p>
“來,給我重復(fù)這句話3遍。。。記住了沒? 好,記住了你就可以關(guān)掉這篇文章了!”
3、小黃書優(yōu)秀閱讀者型
這種類型的特點(diǎn)一般是作者看過《你不知道的JavaScript》書中關(guān)于this的講解,所以通常會(huì)給你一些看著逼格比較高的術(shù)語,
牌面看著比之前的要稍微高一些,
比如:
“隱式綁定。。。balabala”
“顯式綁定。。。balabala”
“硬綁定。。。balabala”
“。。。”
哼,有了這本秘籍,這次一定要好好裝一次逼!
OK, 總結(jié)完畢!
不出意外,少俠你看過的文章,基本應(yīng)該屬于這3種情況之一,
那么,
有的少俠可能要說了,
“沒錯(cuò),都挺好啊,有什么問題嗎?難道你這篇文章還能有什么不一樣?”
對(duì)于這種情況,
我要說的是,
當(dāng)然不一樣!比如標(biāo)題看起來就要囂張一些!
好吧,其實(shí)剛才這幾種類型的文章,確實(shí)也有很多不錯(cuò)的地方,一些想法也很優(yōu)秀。
但是,
他們也忽略掉了一些問題,
比如:
1、為什么會(huì)有this?
2、當(dāng)我們使用this時(shí),我們到底是想做什么?
3、this的重要性有多大?
4、能不能直接避免使用this?
5、使用this有哪些隱患和注意事項(xiàng)?
換句話說,
由于過于關(guān)注了this本身的一些用法和特點(diǎn),
便不太容易從this之外的角度來看待它。
就好比下棋一樣,也許少俠你棋下得已經(jīng)很好了,但是和看棋的人相比,角度肯定不一樣。
而今天,
少俠我們要做的就是,當(dāng)一個(gè)看棋者。
為了弄清楚為什么會(huì)有this,
我們首先要聊的,
是對(duì)象和函數(shù),
對(duì)象可以看做是個(gè)簡單容器,通常來說,你會(huì)使用對(duì)象儲(chǔ)存一些數(shù)據(jù)

比如這里,我們使用一個(gè)對(duì)象儲(chǔ)存了一個(gè)角色信息,一個(gè)叫做天辰dreamer的name信息,和一個(gè)叫做pets的數(shù)組,用來保存寵物信息。
而函數(shù)的話,你通常會(huì)用它來執(zhí)行一些操作,
比如獲取上面的用戶名稱,或者添加,刪除一些寵物等等。

現(xiàn)在對(duì)象有了,函數(shù)也有了,我們就可以結(jié)合起來使用一下,用 addPets 向 user 中添加一個(gè)寵物:

好了,我們使用 addPets 成功向 user 中添加了一個(gè)寵物,也通過 getName 函數(shù)訪問到了 user 的 name。
但是,大多數(shù)情況下,如果 getName 和 addPets 函數(shù)都是和 user 相關(guān)的,
少俠你可能會(huì)傾向于把他們放在一塊兒:

我們把 getName 和 addPets 放在了 user 內(nèi)部,作為了它的方法。
并且經(jīng)過我們測試,結(jié)果也是正常的,
所以沒有問題了吧?
如果少俠你只是這樣使用的話,確實(shí)沒有問題,
但是,假如你現(xiàn)在想增加一個(gè)新角色,除了名字,其他都一樣,你可能會(huì)怎么做呢?
你肯定不想全部都重新寫一遍,因?yàn)橹挥忻植灰粯?,所以你想直接?fù)制一份user,然后改變一下name信息。

這個(gè)時(shí)候,看似好像已經(jīng)成功復(fù)制了一份user,
甚至如果少俠你打印一下user2,你也會(huì)看見它的name屬性是'胖虎dreamer',
那么問題會(huì)出現(xiàn)在哪呢?
問題出現(xiàn)在當(dāng)你使用user2上面的方法時(shí):

造成這個(gè)情況的原因是,我們?cè)趗ser中g(shù)etName函數(shù)內(nèi)部,是在通過作用域訪問user:

addPets方法也是一樣,也就是說,如果我們調(diào)用user2.getPets,里面還是指向的user,
也就是說,實(shí)際上會(huì)把寵物添加到user里面去,
少俠你肯定不希望自己買的東西,被發(fā)送到別人家吧?
要解決這個(gè)問題也行,比如改為每次調(diào)用方法時(shí)我們都手動(dòng)傳遞一個(gè)user進(jìn)去:

這樣的話,倒是勉強(qiáng)能用,但某些少俠可能會(huì)說:
“喂,天辰你傻了吧? 那照你這樣,我還不如直接弄兩個(gè)函數(shù),然后傳遞不同的對(duì)象就行了,我都放一起了,完了還是得我手動(dòng)告訴它們?”
“對(duì)啊對(duì)啊,那我還能這樣使用呢,調(diào)用 user1 的方法,但是傳遞user2進(jìn)去,不僅如此,我還能一會(huì)兒傳遞user,一會(huì)兒又傳遞user2,怎么樣
?怎么樣?”

[圖片上傳失敗...(image-63c6ae-1581322063794)]
沒錯(cuò),作為一個(gè) dreamer,肯定不能采用這么 low 的方式!
所以,我們現(xiàn)在有點(diǎn)糾結(jié),
如果getName采用作用域的方式的話,那么它總是會(huì)訪問最開始定義時(shí)的對(duì)象,
如果getName采用動(dòng)態(tài)傳遞對(duì)象的話,那么就得每次我們手動(dòng)傳遞。
更矛盾的是,既然我每次都得手動(dòng)傳遞的話,我又干嘛要把函數(shù)放在某個(gè)對(duì)象里面呢?
所以我們想要的是什么呢?
我們想要的是,
getName既能動(dòng)態(tài)的切換訪問的對(duì)象,也不需要每次手動(dòng)指定。
比如說我在user上調(diào)用它,它就訪問user,而我在user2上調(diào)用它,它就訪問user2.
換句話說,一次定義,到處使用。
有辦法嗎?
有!
這就是我們今天的主角,this,需要做的事。

beng ~,
神奇的事情發(fā)生了~
當(dāng)我們?cè)?user上面調(diào)用 getName時(shí),getName 內(nèi)部的 this 指向了 user ,
而當(dāng)我們?cè)?user2 內(nèi)部調(diào)用時(shí),getName 內(nèi)部的 this 又神奇的指向了 user2 ,
如果我們需要的話,我們也可以再復(fù)制一份user3:

還是一樣的效果,
甚至我們也可以單獨(dú)把 getName 放在外面:

好了,現(xiàn)在少俠你知道了為什么需要 this 了吧?
也知道了為什么會(huì)動(dòng)態(tài)的變來變?nèi)チ税桑?/p>
那么,如果直接調(diào)用帶有 this 的函數(shù)會(huì)是什么情況呢?
比如:

getThis 的結(jié)果會(huì)是什么呢?
現(xiàn)在我們已經(jīng)的是,
假如把 getThis 放在 user上,然后通過 user 調(diào)用,它會(huì)是 user, 如果通過 user2 調(diào)用,它會(huì)是 user 2,但是這里既沒有 通過 user調(diào)用,也沒有通過 user2 調(diào)用,那么結(jié)果應(yīng)該是什么呢?
在查找答案之前,我們可以先假設(shè)2種情況,
1、既然它沒有顯式的通過對(duì)象調(diào)用,我們給這種單獨(dú)調(diào)用的情況,默認(rèn)通過一個(gè)對(duì)象來調(diào)用,比如全局對(duì)象,window 等等。
這樣 this 就會(huì)是 window對(duì)象。
2、既然它沒有顯式地通過對(duì)象調(diào)用 getThis,那么就當(dāng)做找不到對(duì)象好了。
這樣的話,this 就是 undefined
少俠你認(rèn)為哪一種是對(duì)的呢?
答案是兩種都對(duì),它們都是 JavaScript 目前的處理方式,只不過,一個(gè)是普通模式,一個(gè)是嚴(yán)格模式:

好了,少俠,
這就是 關(guān)于 this 的故事了,
- 你通過哪個(gè)對(duì)象調(diào)用帶有 this 的函數(shù),它里面的 this 就指向誰。
- 如果沒有通過任何對(duì)象調(diào)用,this 就會(huì)是全局對(duì)象或是 undefined。
- 之所以會(huì)有這么奇怪的現(xiàn)象,是因?yàn)橹挥羞@樣,你才能在不同對(duì)象之間復(fù)用方法函數(shù)和數(shù)據(jù)。
- 如果你希望在一開始就確定訪問哪個(gè)對(duì)象的話,你應(yīng)該使用詞法作用域。
如果之前的 this 讓少俠你感到奇怪的話,現(xiàn)在你應(yīng)該理解它了,它實(shí)際上是和詞法作用域相互補(bǔ)充的。
根本沒有什么奇怪的地方,那就是 this 自己想要做的。
關(guān)于 this 的陷阱(part 1):
1、不要直接傳遞一個(gè)對(duì)象方法給回調(diào)函數(shù),因?yàn)槟悴恢浪鼤?huì)怎么調(diào)用這個(gè)函數(shù)。
比如它萬一把你的函數(shù)放在另外一個(gè)對(duì)象上再調(diào)用呢?
(通常不會(huì)這么奇怪,但就算直接調(diào)用,由于沒有通過對(duì)象調(diào)用,所以結(jié)果會(huì)和上面一樣)

所以,把帶有 this 的函數(shù)直接當(dāng)做回調(diào)函數(shù)時(shí),你沒辦法確定它會(huì)被怎么調(diào)用,所以,里面的 this 也是不能確定的,
其中一種解決辦法是,用一個(gè)外部函數(shù)包裹它,然后在這個(gè)函數(shù)內(nèi)部,顯式調(diào)用它:

當(dāng)然,
通過 bind 之類的方式也可以完成,
不過,由于這里我們還沒有遇見它,
所以就先不考慮它了~
2、還記得數(shù)組也是對(duì)象嗎?所以下面的情況也是成立的~

3、箭頭函數(shù)沒有自己的 this
上面的規(guī)則只適用于普通函數(shù), ES6 新增加的箭頭函數(shù)沒有自己的 this,它會(huì)使用外部最近的一個(gè)普通函數(shù)的 this,一直往上找,如果一直沒有普通函數(shù),最后會(huì)是全局對(duì)象。

少俠你可以看到, getUser2 是一個(gè)箭頭函數(shù),所以它會(huì)找尋外部最近的一個(gè)普通函數(shù)的 this, 而它外部最近的一個(gè)普通函數(shù)是 getUser ,所以它會(huì)使用 getUser 內(nèi)部的 this,
假如外部沒有普通函數(shù)函數(shù):

user 就在全局環(huán)境下,而 getUser 外部再?zèng)]有其他函數(shù)了,所以就會(huì)訪問到全局對(duì)象 window。
有沒有覺得挺奇怪?
別急,以后你就會(huì)明白了~
完全OK~
恭喜少俠你又成功發(fā)現(xiàn)并閱讀完了一篇文章,
希望它能夠?qū)δ阌兴鶐椭?/p>
然后,
我們還有一些開始的問題還沒有回答,也有一些內(nèi)容沒有說到,
比如 new 關(guān)鍵字,call, apply, bind,
或者再進(jìn)一步的原型鏈,class 等等~
實(shí)際上,簡單聊一下它們不會(huì)花費(fèi)太多的時(shí)間,
但是,關(guān)于它們,其實(shí)還有很多更有趣的東西,
所以,
江湖路遠(yuǎn),下次有緣再見了,少俠~
一些你可能關(guān)心的問題:
1、開頭花里胡哨的,結(jié)果5個(gè)問題就回答了一個(gè),哼,標(biāo)題黨,是不是太敷衍了?
主要是發(fā)現(xiàn)全部一次寫完的話,確實(shí)會(huì)很長很長,雖然有些東西是長一點(diǎn)比較好,但是文章就還是短一些吧。。。
2、對(duì),還有,你開頭舉的幾個(gè)例子是什么意思?瞧不起我們嗎?。。?/strong>
沒有沒有,主要是想夸你們,夸你們性格細(xì)致有耐心,單純直接沒有心機(jī),并且熱愛學(xué)習(xí)愛看書,對(duì),反正都是夸獎(jiǎng)~
3、為什么箭頭函數(shù)不單純當(dāng)做普通函數(shù)的簡寫呢? 為什么要格外改變內(nèi)部的 this 指向呢? 顯得好奇怪,有什么實(shí)際的用途嗎?
我就知道你們一些人雖然知道箭頭函數(shù) this 指向不太對(duì),但是可能不太清楚為什么要這樣做,
這樣的特性在某些地方確實(shí)有特殊用途,能幫助避開一些坑,不過這里就先不說了,哈哈哈哈哈哈~
4、call,apply 這些不說嗎?還有 new 關(guān)鍵字呢? 原型鏈呢?
這些以后都會(huì)提到的,但順序可能會(huì)和少俠你預(yù)想的不一樣,
少俠你可能以為順序會(huì)是,call,apply,bind,new 關(guān)鍵字,原型鏈。。。balabala。。。
不過,
要不我們?cè)囋嚪催^來看怎么樣?
聲明:本文僅限于瀟灑有趣又很酷的天辰dreamer裝逼使用,未經(jīng)允許,禁止以任何形式轉(zhuǎn)載~
