this綁定規(guī)則優(yōu)先級

現(xiàn)在了解了函數(shù)調(diào)用中this綁定的四條規(guī)則,需要做的是找到函數(shù)的調(diào)用位置并判斷應(yīng)用了哪條規(guī)則船惨。如果調(diào)用位置應(yīng)用多條規(guī)則,就必須給這些規(guī)則設(shè)定優(yōu)先級缕陕。
毫無疑問粱锐,默認(rèn)綁定是四條規(guī)則中最低的,可以暫時先不考慮扛邑。
測試下隱式綁定和顯式綁定優(yōu)先級:

    function foo() {
        console.log(this.a);
    }

    var obj1 = {
        a: 1,
        foo: foo
    }

    var obj2 = {
        a: 2,
        foo: foo
    }

    obj1.foo() // 1
    obj2.foo() // 2

    obj1.foo.call(obj2) // 2
    obj2.foo.call(obj1) // 1

顯式綁定優(yōu)先級更高怜浅,也就是說在判斷時應(yīng)當(dāng)先考慮是否可以應(yīng)用顯式綁定。
測試下new綁定和隱式綁定優(yōu)先級:

    function foo(something) {
        this.a = something
    }

    var obj1 = {
        foo: foo
    }

    var obj2 = {}

    obj1.foo(2)
    console.log(obj1.a) // 2

    obj1.foo.call(obj2, 3)
    console.log(obj2.a) // 3

    var bar = new obj1.foo(4) // new 和 call/apply 無法一起使用蔬崩,因此無法通過new foo.call(obj1)來直接測試
    console.log(obj1.a) // 2

    console.log(bar.a) // 4

new綁定比隱式綁定優(yōu)先級高恶座。接下來就是比較new綁定和顯式綁定?
在看代碼之前沥阳,先回憶下硬綁定是如何工作的跨琳。Function.prototype.bind(...)會創(chuàng)建一個新的包裝函數(shù),這個函數(shù)會忽略當(dāng)前的this綁定(無論綁定的對象時什么)桐罕,并把提供的對象綁定到this上脉让。
這樣看起來硬綁定(也是顯式綁定的一種)似乎b比new綁定的優(yōu)先級更高桂敛。

    function foo(something) {
        this.a = something
    }

    var obj1 = {}

    var bar = foo.bind(obj1)

    bar(2)
    console.log(obj1.a) // 2

    var baz = new bar(3)

    console.log(obj1.a) // 2
    console.log(baz.a) // 3

bar被硬綁定到了obj1上,但是new bar(3)并沒有向我們預(yù)計的那樣把obj1.a的值修改為3溅潜。相反术唬,new修改了硬綁定(到obj1的)調(diào)用bar(...)中的this。因此使用了new綁定滚澜,我們得到了一個名字為baz的新對象粗仓,并且baz.a的值是3。

判斷this

如果要判斷一個運行中函數(shù)this的綁定设捐,那么就需要找到這個函數(shù)的直接調(diào)用位置潦牛。找到之后按照以下規(guī)則判斷this綁定的對象。

  • 1.函數(shù)是否在new中調(diào)用(new綁定)挡育?如果是this綁定的是新創(chuàng)建的對象
    var bar = new foo()
  • 2.函數(shù)是否通過call、apply(顯式綁定)或者硬綁定調(diào)用朴爬?如果是this綁定的是指定的對象
    var bar = foo.call(obj1)
  • 3.函數(shù)是否在某個上下文對象中調(diào)用(隱式綁定)即寒?如果是this綁定的是那個上下文對象
    var bar = obj1.foo()
  • 4.如果都不是,使用默認(rèn)綁定召噩。如果在嚴(yán)格模式下母赵,就綁定到了undefined,否則綁定到了全局對象具滴。
    var bar = foo()
綁定例外

1.被忽略的this
如果你把null或者undefined作為this的綁定對象傳入到call/apply/bind凹嘲,這些值在調(diào)用時會被忽略,實際應(yīng)用的會是默認(rèn)綁定

function foo() {
    console.log(this.a)
}
var a = 2;
foo.call(null) // 2

什么情況下你會傳入null构韵?
一種非常常見的做法是使用apply(...)來“展開”一個數(shù)組周蹭,并當(dāng)做參數(shù)傳入一個函數(shù)。類似的疲恢,bind(...)可以對參數(shù)進行kelihua柯里化(預(yù)先設(shè)置一些參數(shù))凶朗,非常有用:

    function foo(a, b) {
        console.log("a: " + a + ", b:" + b)
    }

    // 把數(shù)組展開成參數(shù)
    foo.apply(null, [2, 3]) // a: 2, b:3
    // 使用bind(...)進行柯里化
    var baz = foo.bind(null, 4)
    baz(5) //a: 4, b:5

這兩種方法都需要傳入一個參數(shù)當(dāng)做this的綁定對象。如果函數(shù)不關(guān)心this的話显拳,你仍然需要傳入一個占位值棚愤,這時null就是個不錯的選擇。
使用null帶來的副作用是杂数,如果某個函數(shù)確實使用了this宛畦,那么默認(rèn)綁定會把this綁定到全局對象中。(可以使用Object.create(null)來代替null更安全)

2.間接引用
需要注意的是揍移,可能創(chuàng)建一個函數(shù)的間接引用次和,這種情況下會應(yīng)用默認(rèn)綁定

    function foo() {
        console.log(this.a)
    }

    var a = 2;
    var o = {a: 3, foo: foo}

    var p = {a: 4}

    o.foo(); //3

    (p.foo = o.foo)(); // 4

賦值表達(dá)式p.foo = o.foo的返回值是目標(biāo)函數(shù)的引用,因此調(diào)用位置是foo()而不是p.foo()或者o.foo()那伐,所以會應(yīng)用默認(rèn)綁定斯够。
注意:對于默認(rèn)綁定來說囚玫,決定this綁定對象的并不是調(diào)用位置是否處于嚴(yán)格模式,而是函數(shù)體是否處于嚴(yán)格模式读规,嚴(yán)格模式下this綁定到undefined抓督,否則this綁定到全局對象

3.軟綁定

this詞法

ES6中介紹了一種無法使用這些規(guī)則的特殊函數(shù):箭頭函數(shù)。
箭頭函數(shù)并不是使用function定義的束亏,箭頭函數(shù)不適用這四種標(biāo)準(zhǔn)規(guī)則铃在,而是根據(jù)外層作用域來決定this。(箭頭函數(shù)會繼承外層函數(shù)調(diào)用的this綁定)

    function foo() {
        return (a) => {
            // this繼承自foo
            console.log(this.a)
        }
    }

    var obj1 = {
        a: 2
    }

    var obj2 = {
        a: 3
    }

    var bar = foo.call(obj1)

    bar.call(obj2) // 2

foo內(nèi)部創(chuàng)建的箭頭函數(shù)會捕獲調(diào)用foo()的this碍遍,由于foo()的this綁定到的是obj1定铜,bar(引用箭頭函數(shù))的this也會綁定到obj1,箭頭函數(shù)的綁定無法修改(new也不行)
箭頭函數(shù)最常用于回調(diào)函數(shù)中怕敬,例如事件處理器或者定時器:

function foo() {
    setTimeout(() => {
        // 這里的this在詞法上繼承自foo()
        console.log(this.a)
    }, 100)
}

var obj = {
  a: 2
}

foo.call(obj) // 2

箭頭函數(shù)可以像bind(...)一樣確保函數(shù)的this被綁定到指定對象揣炕。此外,其重要性還體現(xiàn)在它用更常見的詞法作用域取代了傳統(tǒng)的this機制东跪。實際上畸陡,ES6之前我們就已經(jīng)在使用一種幾乎和箭頭函數(shù)一樣模式

function foo() {
    var self = this;
    setTimeout(function() {
        // 這里的this在詞法上繼承自foo()
        console.log(self.a)
    }, 100)
}

var obj = {
  a: 2
}

foo.call(obj) // 2

雖然self = this和箭頭函數(shù)看起來都可以取代bind(...),但從本質(zhì)上來說他們想取代的是this機制。
如果你經(jīng)常編寫this風(fēng)格的代碼虽填,但絕大部分都會使用self = this或者箭頭函數(shù)來否定this機制丁恭,那你或許應(yīng)當(dāng):

  • 1.只使用詞法作用域并完全拋棄this風(fēng)格代碼
  • 2.完全采取this風(fēng)格,在必要時使用bind(...),盡量避免self=this和箭頭函數(shù)斋日。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牲览,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子恶守,更是在濱河造成了極大的恐慌第献,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兔港,死亡現(xiàn)場離奇詭異痊硕,居然都是意外死亡,警方通過查閱死者的電腦和手機押框,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門岔绸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人橡伞,你說我怎么就攤上這事盒揉。” “怎么了兑徘?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵刚盈,是天一觀的道長。 經(jīng)常有香客問我挂脑,道長藕漱,這世上最難降的妖魔是什么欲侮? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮肋联,結(jié)果婚禮上威蕉,老公的妹妹穿的比我還像新娘。我一直安慰自己橄仍,他們只是感情好韧涨,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侮繁,像睡著了一般虑粥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宪哩,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天娩贷,我揣著相機與錄音,去河邊找鬼锁孟。 笑死彬祖,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罗岖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼腹躁,長吁一口氣:“原來是場噩夢啊……” “哼桑包!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纺非,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤哑了,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后烧颖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弱左,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年炕淮,在試婚紗的時候發(fā)現(xiàn)自己被綠了拆火。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡涂圆,死狀恐怖们镜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情润歉,我是刑警寧澤模狭,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站踩衩,受9級特大地震影響嚼鹉,放射性物質(zhì)發(fā)生泄漏贩汉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一锚赤、第九天 我趴在偏房一處隱蔽的房頂上張望匹舞。 院中可真熱鬧,春花似錦宴树、人聲如沸策菜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽又憨。三九已至,卻和暖如春锭吨,著一層夾襖步出監(jiān)牢的瞬間蠢莺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工零如, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躏将,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓考蕾,卻偏偏與公主長得像祸憋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子肖卧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

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

  • 特別說明塞帐,為便于查閱拦赠,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 693評論 0 1
  • 關(guān)于 this this 關(guān)鍵字是 JavaScript 中最復(fù)雜的機制之一。它是一個很特別的關(guān)鍵字葵姥,被自動定義在...
    游學(xué)者灬墨槿閱讀 572評論 1 2
  • 1.1 關(guān)于 this 當(dāng)模式越來越復(fù)雜時荷鼠,顯示傳遞上下文對象會讓代碼變得越來越混亂,使用this則不會這樣榔幸≡世郑可自...
    將軍肚閱讀 634評論 0 51
  • 1、調(diào)用位置 在理解this的綁定之前削咆,首先理解調(diào)用位置,決定this的綁定 function a() { // ...
    winerss閱讀 304評論 0 0
  • 今晚逛朋友圈突然有種逛花園的感覺态辛,鮮花點綴麸澜,花香彌漫。小宜發(fā)來一組玫瑰花卷照片引來眾多文友圍觀奏黑,玫紅與金黃的花卷面...
    夜含閱讀 714評論 6 8