深刻理解 Javascript 的 this 指向

現(xiàn)象

首先我們來看一段代碼

let a = 2;
let obj = {
  a: 1,
  foo: function () { console.log(this.a) }
};

let foo = obj.foo;
// 寫法一
obj.foo()
// 寫法二
foo()

寫法一和寫法二的執(zhí)行結(jié)果分別是:1伍宦、2芽死。之所以運行結(jié)果不用,是因為方法中使用了 this 關(guān)鍵字次洼。this 指代方法調(diào)用者关贵,即運行時的環(huán)境。對于obj.foo()卖毁,foo 運行在 obj 中揖曾,所以 this 指向 obj。而 foo()亥啦,foo 運行在全局炭剪,所以 this 指向全局。
由此得出結(jié)論:

  • 普通函數(shù)的 this 總是指向它的直接調(diào)用者翔脱。
  • 在嚴格模式下奴拦,沒找到直接調(diào)用者,則函數(shù)中的 this 是 undefined届吁。
  • 在默認模式下(非嚴格模式)错妖,沒找到直接調(diào)用者,則函數(shù)中的 this 指向 window疚沐。

this 的由來

JavaScript 語言之所以有 this 的設(shè)計暂氯,跟內(nèi)存里面的數(shù)據(jù)結(jié)構(gòu)有關(guān)系。

內(nèi)存數(shù)據(jù)結(jié)果

var obj = { foo:  5 };

上面的代碼將一個對象賦值給變量obj亮蛔。Javascript 引擎會先在內(nèi)存里面校读,生成一個對象{ foo: 5 }司倚,然后把這個對象的內(nèi)存地址賦值給變量obj良价。
也就是說榆纽,變量obj是一個地址(reference)。后面如果要讀取obj.foo芬探,引擎先從obj拿到內(nèi)存地址齿尽,然后再從該地址讀出原始的對象,返回它的foo屬性灯节。
原始的對象以字典結(jié)構(gòu)保存,每一個屬性名都對應(yīng)一個屬性描述對象。舉例來說炎疆,上面例子的foo屬性卡骂,實際上是以下面的形式保存的。

{
  foo: {
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}

注意形入,foo屬性的值保存在屬性描述對象的value屬性里面全跨。

函數(shù)

這樣的結(jié)構(gòu)是很清晰的,問題在于屬性的值可能是一個函數(shù)亿遂。

var obj = { foo: function () {} };

這時浓若,引擎會將函數(shù)單獨保存在內(nèi)存中,然后再將函數(shù)的地址賦值給foo屬性的value屬性蛇数。

{
  foo: {
    [[value]]: 函數(shù)的地址
    ...
  }
}

由于函數(shù)是一個單獨的值挪钓,所以它可以在不同的環(huán)境(上下文)執(zhí)行。

var f = function () {};
var obj = { f: f };

// 單獨執(zhí)行
f()

// obj 環(huán)境執(zhí)行
obj.f()

環(huán)境變量

Javascript 允許在函數(shù)體內(nèi)部耳舅,引用當(dāng)前環(huán)境的其他變量碌上。

var f = function () {
  console.log(x);
};

上面代碼中,函數(shù)體里面使用了變量x浦徊。該變量由運行環(huán)境提供馏予。
現(xiàn)在問題就來了,由于函數(shù)可以在不同的運行環(huán)境執(zhí)行盔性,所以需要有一種機制霞丧,能夠在函數(shù)體內(nèi)部獲得當(dāng)前的運行環(huán)境(context)。所以冕香,this就出現(xiàn)了蛹尝,它的設(shè)計目的就是在函數(shù)體內(nèi)部,指代函數(shù)當(dāng)前的運行環(huán)境暂筝。

var f = function () {
  console.log(this.x);
}

上面代碼中箩言,函數(shù)體里面的this.x就是指當(dāng)前運行環(huán)境的x

var f = function () {
  console.log(this.x);
}

var x = 1;
var obj = {
  f: f,
  x: 2,
};

// 單獨執(zhí)行
f() // 1

// obj 環(huán)境執(zhí)行
obj.f() // 2

上面代碼中焕襟,函數(shù)f在全局環(huán)境執(zhí)行陨收,this.x指向全局環(huán)境的x

avatar

obj環(huán)境執(zhí)行鸵赖,this.x指向obj.x务漩。
avatar

回到本文開頭提出的問題,obj.foo()是通過obj找到foo它褪,所以就是在obj環(huán)境執(zhí)行饵骨。一旦var foo = obj.foo,變量foo就直接指向函數(shù)本身茫打,所以foo()就變成在全局環(huán)境執(zhí)行居触。

箭頭函數(shù)

在 ES6 中新增的箭頭函數(shù)妖混,不僅簡化了代碼,還解決 this 飄忽不定的指向問題轮洋。

var obj = {
    a : 1,
    foo : function(){
        setTimeout(
            function(){console.log(this.a),3000})
    }
}
obj.foo(); //undefined

此代碼運行結(jié)果為 undefined制市,this 的指向是全局的 window 對象。

()=>{console.log(this)}

其中()內(nèi)是要帶入的參數(shù)弊予,{}內(nèi)是要執(zhí)行的語句祥楣。箭頭函數(shù)是函數(shù)式編程的一種體現(xiàn),函數(shù)式編程將更多的關(guān)注點放在輸入和輸出的關(guān)系汉柒,省去了過程的一些因素误褪,因此箭頭函數(shù)中沒有自己的 this,arguments碾褂,new target(ES6)和 super(ES6)兽间。箭頭函數(shù)相當(dāng)于匿名函數(shù),因此不能使用new來作為構(gòu)造函數(shù)使用斋扰。
箭頭函數(shù)中的 this 始終指向其父級作用域中的 this 渡八。換句話說,箭頭函數(shù)會捕獲其所在的上下文的 this 值传货,作為自己的 this 值屎鳍。任何方法都改變不了其指向,如 call(), bind(), apply()问裕。在箭頭函數(shù)中調(diào)用 this 時逮壁,僅僅是簡單的沿著作用域鏈向上尋找,找到最近的一個 this 拿來使用粮宛,它與調(diào)用時的上下文無關(guān)窥淆。我們用代碼來解釋一下。

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巍杈,一起剝皮案震驚了整個濱河市忧饭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筷畦,老刑警劉巖词裤,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鳖宾,居然都是意外死亡吼砂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門鼎文,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渔肩,“玉大人,你說我怎么就攤上這事拇惋≈苜耍” “怎么了抹剩?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蓉坎。 經(jīng)常有香客問我吧兔,道長,這世上最難降的妖魔是什么袍嬉? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮灶平,結(jié)果婚禮上伺通,老公的妹妹穿的比我還像新娘。我一直安慰自己逢享,他們只是感情好罐监,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞒爬,像睡著了一般弓柱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侧但,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天矢空,我揣著相機與錄音,去河邊找鬼禀横。 笑死屁药,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柏锄。 我是一名探鬼主播酿箭,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼趾娃!你這毒婦竟也來了缭嫡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤抬闷,失蹤者是張志新(化名)和其女友劉穎妇蛀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饶氏,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡讥耗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疹启。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片古程。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖喊崖,靈堂內(nèi)的尸體忽然破棺而出挣磨,到底是詐尸還是另有隱情雇逞,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布茁裙,位于F島的核電站塘砸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏晤锥。R本人自食惡果不足惜掉蔬,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矾瘾。 院中可真熱鬧女轿,春花似錦、人聲如沸壕翩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽放妈。三九已至北救,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芜抒,已是汗流浹背珍策。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挽绩,地道東北人膛壹。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像唉堪,于是被迫代替她去往敵國和親模聋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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

  • 1.含義 this就是屬性或方法“當(dāng)前”所在的對象唠亚。 上面代碼中链方,this.name表示name屬性所在的那個對象...
    Kevin丶CK閱讀 792評論 0 3
  • this是 JavaScript 語言的一個關(guān)鍵字。 它是函數(shù)運行時灶搜,在函數(shù)體內(nèi)部自動生成的一個對象祟蚀,只能在函數(shù)體...
    ERICOOLU閱讀 447評論 0 0
  • 談及 Javascript 中的 this,竟然讓人覺得頭疼割卖,它不像 Java前酿,C++ 中的 this 指向調(diào)用 ...
    七色煙火閱讀 415評論 0 0
  • 1.概念 在JavaScript中,this 是指當(dāng)前函數(shù)中正在執(zhí)行的上下文環(huán)境鹏溯,因為這門語言擁有四種不同的函數(shù)調(diào)...
    BluesCurry閱讀 1,122評論 0 2
  • 將中文的url編碼成utf8 將utf8編碼轉(zhuǎn)換成漢字 應(yīng)該是全局的方法
    千罹閱讀 367評論 0 0