是時候看清JS 中的 this 了

this 到底是個什么東西

MDN 上對于 this 如此定義:

A property of an execution context (global, function or eval) that, in non–strict mode, is always a reference to an object and in strict mode can be any value.

上面這段話,主要講的是 this 的存在形式庙洼,這當然不太夠斑响,也許我們更想要搞明白的是瞧挤,它是為了什么存在屉凯。

事實上父虑,當我們在獲取一個變量的時候母赵,我們想要知道逸爵,它是誰的?當我們執(zhí)行一個函數(shù)的時候凹嘲,我們又得知道师倔,這一次函數(shù)的執(zhí)行,到底是針對誰的周蹭?

我想趋艘,這就是 this。

如何確定 this? call, bind, apply

它在非嚴格模式下總是一個對象凶朗,在嚴格模式下瓷胧,可以是任何值。

當我們沒有特別指定 this 的時候棚愤,this 實際上就是它所出現(xiàn)在的執(zhí)行環(huán)境所對應的對象搓萧,我們在 window 中寫個 this,代碼跑到這里的時候宛畦,編譯器就知道瘸洛,這個 this 就是指 window.

而如果我們想要(需要)主動設(shè)置 this …… 那,就要深入談一談 call / bind / apply 了

這三個方法主要是用于函數(shù)的次和,當我們調(diào)用一個函數(shù)的時候反肋,這個函數(shù)的這一次執(zhí)行,究竟是“針對誰”斯够,這是每個函數(shù)在每一次執(zhí)行的時候都要考慮的囚玫。我們所習以為常的 fn() 實際上是一個語法糖,當我們在 window 里面執(zhí)行這個函數(shù)的時候读规,它的原始面貌是:
fn.call(window)

call / bind / apply 的第一參數(shù)就的 this.

也就是說抓督,如果我們不偷懶的話, fn.call 才是真正的“函數(shù)調(diào)用”(即束亏,調(diào)用了函數(shù)的 call 方法)铃在,我們每次調(diào)用函數(shù)的時候都要告訴函數(shù) this 是什么。這里碍遍,我們只比較 call 與 bind定铜,因為,apply 與 call 很類似怕敬,只是參數(shù)的形式不一樣揣炕。
bind 是什么呢?當 bind 出現(xiàn)的時候东跪,實際上表達的意思是 “我要重新創(chuàng)一個看起來一樣的函數(shù)畸陡,但是這個函數(shù)執(zhí)行的時候 this 永遠指向我最初指定的鹰溜。”
所以丁恭,fn.bind(window) 得到曹动,不是函數(shù)的執(zhí)行,而是一個 this 被“綁”死了的函數(shù)牲览。那么墓陈,它與 call 的差異顯而易見: call 是執(zhí)行函數(shù),bind 是創(chuàng)建函數(shù)第献,就算是 bind 生成的函數(shù)在執(zhí)行的時候贡必,依然需要用到 call 方法,只不過痊硕,call 指定的 this 是無效的赊级。

let b = fn.bind(window)
b.call(a) // 實際還是 b.call(window),因為 b 變量對應的函數(shù)岔绸,在創(chuàng)建的時候 this 已經(jīng) bind.

這一點有什么用呢理逊?舉個例子:
MDN 中對于數(shù)組的 slice 方法有一個描述,

slice method can also be called to convert Array-like objects / collections to a new Array.
slice 方法可以接收一個類數(shù)組盒揉,返回一個新數(shù)組晋被。

于是我們就可以構(gòu)建一個“數(shù)組轉(zhuǎn)換器”:

let slice = Function.prototype.call.bind(Array.prototype.slice)
// 這是 MDN 中的例子

你沒看錯,我們在 call 后面在接一個 bind刚盈,因為 call 也是一個函數(shù)羡洛,
這段代碼翻譯成人話就是,slice 這個變量藕漱,被賦值了一個 call 函數(shù)欲侮,這個 call 函數(shù)的 this 綁定了 Array.prototype.slice
(有意思,不只是普通函數(shù)有 this肋联,call 函數(shù)也有 this 的)
于是威蕉,我們就可以用了:

slice({0:"a", 1:"b", length:2}) // → ["a", "b"]

好處是,上面這段代碼就等價于:

Array.prototype.slice({0:"a", 1:"b", length:2})
//為了對比橄仍,我們采用不用 call 的語法糖寫法

你看韧涨,這一下子就省了1… 2… 3 …… 好多好多字符呢~

總結(jié)下來,每當我們執(zhí)行一個函數(shù)(非箭頭函數(shù))的時候侮繁,只要看一看這個函數(shù)相關(guān)的 bind虑粥,再把它轉(zhuǎn)化為 fn.call(this) 的形式就能夠很好確定 this 了。

對了宪哩,語法糖寫法的 this 就是被調(diào)用的函數(shù)前面的一大堆東西:

a.b.c.fn() // 等價于 a.b.c.fn.call(a.b.c)

用 class 生成對象時的 this

官方說了算:this 指向新生成的對象

arrow funcion

官方說娩贷,箭頭函數(shù)里面的 this 由箭頭函數(shù)執(zhí)行時的環(huán)境 this 決定,換而言之锁孟,箭頭函數(shù)出不出現(xiàn)育勺,并不影響 this 的指向但荤,我們不需要告訴箭頭函數(shù)它是針對誰罗岖,反正它很“隨遇而安” ……

他們說了算 ……

其它

是的涧至,總是“他們說了算”,當我們調(diào)用任何一個 API 的時候桑包,實際的代碼對于我們來說就好像是藏在“黑匣子”里南蓬,this 是什么,只有開發(fā)人員知道:

dom.onclick = fn

在dom API 的事件觸發(fā)函數(shù)中哑了, this 是觸發(fā)當前事件的元素赘方。

再比如,React 在調(diào)用 onClick 的時候弱左,會強制把 this 指向 undefined …… 所以我們在 React 中做父子通信的時候常常要用到 bind窄陡。

嚴格模式

還是看回最初的那段話:

A property of an execution context (global, function or eval) that, in non–strict mode, is always a reference to an object and in strict mode can be any value.

它是執(zhí)行環(huán)境下的一個屬性,在非嚴格模式下拆火,總是指向?qū)ο筇玻趪栏衲J较拢梢允侨魏沃怠?br> 非常有意思们镜,在非嚴格模式下币叹,我們嘗試把 this 指向 字符串、數(shù)字模狭、布爾值 的時候颈抚,瀏覽器會把這些值轉(zhuǎn)換為對象的形式。
而如果我們嘗試把 this 指向 undefined, null 的時候嚼鹉,this 會變成 window.

this1.png

而在嚴格模式下贩汉,我們指定 this 是什么,它就真的是什么

寫在最后

在討論 bind & call 的時候锚赤,我們就注意到一個有去的想象:bind / call 本身就是函數(shù)匹舞,那么他們有沒有 call, bind 函數(shù)呢?肯定有宴树,因為 javascript 中這些方法都只不過是一個繼承

this2.png

就像最初的例子策菜, call.bind 執(zhí)行,call.call 也存在酒贬,之所以 fn.call.call() 會報錯又憨,那是因為,默認情況下锭吨,使用 call 又沒輸入 this 參數(shù)的時候蠢莺,this 默認是 window, 而 call 的 this 必須是一個函數(shù)(別忘了,函數(shù)是一類特殊的對象):

this3.png

其實還有點什么想說

當我在思考 this 的時候零如,我總不由自主地受到“作用域”的影響躏将。
現(xiàn)在看來锄弱,還是挺明了的:this 跟作用域關(guān)系不大的,作用域討論的是函數(shù)的固有屬性祸憋,而在函數(shù)里面討論到 this 是在確定函數(shù)某一次執(zhí)行的特定歸屬 ……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末会宪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蚯窥,更是在濱河造成了極大的恐慌掸鹅,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拦赠,死亡現(xiàn)場離奇詭異巍沙,居然都是意外死亡,警方通過查閱死者的電腦和手機荷鼠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門句携,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人允乐,你說我怎么就攤上這事矮嫉。” “怎么了喳篇?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵敞临,是天一觀的道長。 經(jīng)常有香客問我麸澜,道長挺尿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任炊邦,我火速辦了婚禮编矾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘馁害。我一直安慰自己窄俏,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布碘菜。 她就那樣靜靜地躺著凹蜈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪忍啸。 梳的紋絲不亂的頭發(fā)上仰坦,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音计雌,去河邊找鬼悄晃。 笑死,一個胖子當著我的面吹牛凿滤,可吹牛的內(nèi)容都是我干的妈橄。 我是一名探鬼主播庶近,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼眷蚓!你這毒婦竟也來了鼻种?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤溪椎,失蹤者是張志新(化名)和其女友劉穎普舆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體校读,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年祖能,在試婚紗的時候發(fā)現(xiàn)自己被綠了歉秫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡养铸,死狀恐怖雁芙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钞螟,我是刑警寧澤兔甘,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站鳞滨,受9級特大地震影響洞焙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拯啦,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一澡匪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧褒链,春花似錦唁情、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至兵迅,卻和暖如春抢韭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喷兼。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工篮绰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人季惯。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓吠各,卻偏偏與公主長得像臀突,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子贾漏,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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