JS中函數(shù)內(nèi)部this的探究

本文參考鏈接 徹底理解js中this的指向知乎回答 以及 JavaScript中的對象查找

下面所說的全部為標(biāo)準(zhǔn)模式下的情況,至于嚴(yán)格模式下的this捺球,請參閱另一篇文章最后一部分內(nèi)容

首先必須要說的是,this的指向在函數(shù)定義的時候是確定不了的嘶是,只有函數(shù)執(zhí)行的時候才能確定this到底指向誰行拢,實際上this的最終指向的是那個調(diào)用它的對象。

既然this最終指向調(diào)用它的對象痕囱,那么我就首先看下田轧,JS中函數(shù)的調(diào)用分那幾種情況。

JS中函數(shù)調(diào)用共分為四種類型

一:Function Invocation Pattern

諸如foo()的調(diào)用形式被稱為Function Invocation Pattern鞍恢,是函數(shù)最直接的使用形式傻粘,注意這里的foo是作為單獨的變量出現(xiàn),而不是屬性帮掉,前面并無調(diào)用對象弦悉。
在這種模式下,foo函數(shù)體中的this永遠(yuǎn)為Global對象蟆炊,在瀏覽器中就是window對象稽莉。比如下面的代碼:

function foo(){
    var user = "追夢子";
    console.log(this.user); //undefined
    console.log(this); //Window
}
foo();

即使在函數(shù)內(nèi)部調(diào)用,由于前面并無調(diào)用對象涩搓,this還是指向window全局對象污秆,如以下代碼

function printThis() {
  console.log(this)
  let print = function () { console.log(this) };
  print();  
}
printThis()  // 第一行打印 window,第二行打印window
printThis.call([1]);  // 第一行打印[1]昧甘,第二行打印window
printThis.call([2]);  // 第一行打印[2]混狠,第二行打印window

在上面的代碼中,第一條將打印printThis的this疾层,當(dāng)通過call函數(shù)的時候将饺,參數(shù)作為this傳遞到函數(shù)中,因此分別打印[1]和[2]痛黎。第二條因為沒有對象調(diào)用print予弧,因此一直打印window。
即使將print改為匿名函數(shù)湖饱,比如下面的代碼掖蛤,結(jié)果依然沒有變化。因此此時匿名函數(shù)依然由window調(diào)用

function printThis() {
  console.log(this)
  (function () {
    console.log(this)
  })(); // 匿名函數(shù)和上面的print都由window調(diào)用
}
printThis()  // 第一行打印 window井厌,第二行打印window
printThis.call([1]);  // 第一行打印[1]蚓庭,第二行打印window
printThis.call([2]);  // 第一行打印[2],第二行打印window

但是在ES6中仅仆,隨著箭頭函數(shù)的引入器赞,這種情況有所變化,將上面的函數(shù)改為下面的格式:

function printThis() {
  console.log(this)
  let print = () => console.log(this);
  print();
}
printThis()  // 第一行打印 window墓拜,第二行打印window
printThis.call([1]);  // 第一行打印[1]港柜,第二行打印[1]
printThis.call([2]);  // 第一行打印[2],第二行打印[2]

此時的結(jié)果和上面就有所不同,這是因為在箭頭函數(shù)中夏醉,拋棄了自己的this屬性爽锥,而是直接使用封閉執(zhí)行上下文的this值。所謂的封閉執(zhí)行上下文畔柔,就是箭頭函數(shù)出現(xiàn)的地方的代碼域氯夷。此時第一條和第二條打印內(nèi)容相同。
在箭頭函數(shù)中靶擦,所有的this原則肠槽,無論是標(biāo)準(zhǔn)模式還是嚴(yán)格模式下,都不再生效奢啥。

二:Method Invocation Pattern

諸如foo.bar()的調(diào)用形式被稱為Method Invocation Pattern秸仙,注意其特點是被調(diào)用的函數(shù)作為一個對象的屬性出現(xiàn),必然會有“.”或者“[]”這樣的關(guān)鍵符號桩盲。
在這種模式下寂纪,bar函數(shù)體中的this永遠(yuǎn)為“.”或“[”前的那個對象,如上例中就一定是foo對象赌结。比如下面代碼:

var o = {
  a:10,
  b:{
    a:12,
    fn:function(){
      console.log(this.a); //12
    }
  }
}
o.b.fn(); 

雖然首先調(diào)用的對象為o捞蛋,但最終的函數(shù)fn是對象b的一個屬性,因此this.a為12柬姚。
然后我們再看下下面的代碼

var o = {
  a:10,
  b:{
    a:12,
    fn:function(){
      console.log(this.a); //12
    }
  }
}
var j = o.b.fn;  // 只是定義拟杉,并未調(diào)用執(zhí)行,真正的調(diào)用執(zhí)行在下面的j()
j(); 

此時的this.a應(yīng)該是多少呢量承?此時的this其實為window搬设,this.a為undefined。為什么如此呢撕捍?回顧一下拿穴,在文章的最開始,我們就提到了this的最終指向的是那個調(diào)用它的對象忧风。而我們在使用o.b.fn的時候默色,并沒有調(diào)用執(zhí)行,真正的調(diào)用執(zhí)行是在下面的j()狮腿,此時的情況和第一種方式一樣腿宰。

三:Constructor Pattern

new foo()這種形式的調(diào)用被稱為Constructor Pattern,其關(guān)鍵字new就很能說明問題缘厢,非常容易識別吃度。
在這種模式下,foo函數(shù)內(nèi)部的this永遠(yuǎn)是new foo()返回的對象昧绣。比如下面的代碼:

function Foo () {
  this.x = 1;
}
Foo.prototype.print = function () {
  console.log(this.x);
}

let foo = new Foo();
foo.print(); 
foo.print.call({x: 2});

此時规肴,第一條打印為1捶闸,表示this為foo實例對象夜畴。this.x為constructor函數(shù)中賦值的1拖刃。第二條打印為2,因此為使用了{x: 2}作為對象替換了foo實例對象中的this贪绘。
在這里需要注意Function創(chuàng)建對象的一種特殊情況

function Foo () {
  this.x = 1;
  return {x: 2};
}
Foo.prototype.print = function () {
  console.log(this.x);
}
let foo = new Foo();
console.log(foo.x);  // 2
foo.print();  // funciton print undefined

如果構(gòu)建函數(shù)Foo返回的是一個對象兑牡,那么foo就會被這個對象所替換掉,此時foo為{x: 2}税灌,其中只有一個x屬性等于2均函,并無print這個方法屬性。但是如果我們修改下代碼菱涤,改成下面的格式:

function Foo () {
  this.x = 1;
  return 1;
}
Foo.prototype.print = function () {
  console.log(this.x);
}
let foo = new Foo();
console.log(foo.x);  // 1
foo.print();  // 1

因為構(gòu)建函數(shù)Foo返回的是并不是一個對象苞也,那么foo就不會被替換掉,依然是Foo的一個實例對象粘秆,此時的foo.xthis.x全部為1如迟。

四:Apply Pattern

foo.call(thisObject)foo.apply(thisObject)的形式被稱為Apply Pattern,使用了內(nèi)置的callapply函數(shù)攻走。
在這種模式下殷勘,callapply的第一個參數(shù)就是foo函數(shù)體內(nèi)的this,如果thisObject是nullundefined昔搂,那么會變成window對象玲销。具體代碼,我們在上面的三種模式中已經(jīng)順帶闡述摘符,因此不再贅述贤斜。


練習(xí)部分

查看下下面的代碼,分析下打印的結(jié)果逛裤,然后實際運(yùn)行下蠢古,看下是否和結(jié)果一致

var x = 0;
function Foo () {
  this.x = 1;
}
Foo.prototype.print = function () {
  console.log(this);
  console.log(this.x);
  (function () {
    console.log(this);
    console.log(this.x)
  })()
}

let foo = new Foo();
foo.print.call({x: 2});

查看代碼,首先注意到Foo對象的print中别凹,第三條和第四條打印是在一個匿名函數(shù)中草讶,此時該匿名函數(shù)的調(diào)用者為window全局變量,因此第四條打印中的this.xwindow.x = 0炉菲。再往下看堕战,發(fā)現(xiàn)使用{x:2}代替了print函數(shù)中的this,因此第一條打印為{x: 2}拍霜,第二條打印為2嘱丢。

如果修改下代碼,將print的定義改為箭頭函數(shù)呢祠饺?結(jié)果又如何越驻?代碼如下:

var x = 0;
function Foo () {
  this.x = 1;
}
Foo.prototype.print = () => {
  console.log(this);
  console.log(this.x);
  (function () {
    console.log(this);
    console.log(this.x)
  })()
}

let foo = new Foo();
foo.print.call({x: 2});

首先,我們知道箭頭函數(shù)不包含this,它的this為執(zhí)行上下文中的this缀旁。而且箭頭函數(shù)的執(zhí)行上下文的判定记劈,就在其定義的時刻決定,我們發(fā)現(xiàn)并巍,它是在腳本中定義的目木,此時的作用域為全局,因此此時懊渡,前兩條打印和后兩條打印一樣刽射,都為0和window。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剃执,一起剝皮案震驚了整個濱河市誓禁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肾档,老刑警劉巖现横,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異阁最,居然都是意外死亡戒祠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門速种,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姜盈,“玉大人,你說我怎么就攤上這事配阵×笏蹋” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵棋傍,是天一觀的道長救拉。 經(jīng)常有香客問我,道長瘫拣,這世上最難降的妖魔是什么亿絮? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮麸拄,結(jié)果婚禮上派昧,老公的妹妹穿的比我還像新娘。我一直安慰自己拢切,他們只是感情好蒂萎,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淮椰,像睡著了一般五慈。 火紅的嫁衣襯著肌膚如雪纳寂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天泻拦,我揣著相機(jī)與錄音毙芜,去河邊找鬼。 笑死聪轿,一個胖子當(dāng)著我的面吹牛爷肝,可吹牛的內(nèi)容都是我干的猾浦。 我是一名探鬼主播陆错,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼金赦!你這毒婦竟也來了音瓷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤夹抗,失蹤者是張志新(化名)和其女友劉穎绳慎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漠烧,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡杏愤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了已脓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珊楼。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖度液,靈堂內(nèi)的尸體忽然破棺而出厕宗,到底是詐尸還是另有隱情,我是刑警寧澤堕担,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布已慢,位于F島的核電站,受9級特大地震影響霹购,放射性物質(zhì)發(fā)生泄漏佑惠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一齐疙、第九天 我趴在偏房一處隱蔽的房頂上張望兢仰。 院中可真熱鬧,春花似錦剂碴、人聲如沸把将。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽察蹲。三九已至请垛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間洽议,已是汗流浹背宗收。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留亚兄,地道東北人混稽。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像审胚,于是被迫代替她去往敵國和親匈勋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理膳叨,服務(wù)發(fā)現(xiàn)洽洁,斷路器,智...
    卡卡羅2017閱讀 134,699評論 18 139
  • 函數(shù)參數(shù)的默認(rèn)值 基本用法 在ES6之前菲嘴,不能直接為函數(shù)的參數(shù)指定默認(rèn)值饿自,只能采用變通的方法。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,402評論 0 1
  • 1.概念 在JavaScript中龄坪,this 是指當(dāng)前函數(shù)中正在執(zhí)行的上下文環(huán)境昭雌,因為這門語言擁有四種不同的函數(shù)調(diào)...
    BluesCurry閱讀 1,135評論 0 2
  • 函數(shù)和對象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對于任何一門語言來說都是核心的概念健田。通過函數(shù)可以封裝任意多條語句烛卧,而且...
    道無虛閱讀 4,581評論 0 5
  • 大多數(shù)人都是需要口語和聽力唱星,單詞量又是基礎(chǔ),如何兼顧跟磨? 每天一篇聽力间聊,和跟讀。 單詞不用刻意背抵拘,堅持讀書哎榴。 有病就...
    Nick_k哥閱讀 241評論 0 0