說說JS中關(guān)于this指向這件事

引言

最近在學(xué)習(xí)的過程中發(fā)現(xiàn)越來越多的地方使用到this,在此之前我對this的指向知之甚少周叮,由此決定深究一一番欺矫,做下記錄以備今后復(fù)習(xí)之用,文章內(nèi)所有代碼執(zhí)行結(jié)果只針對瀏覽器環(huán)境。

this在做代詞時翻譯成中文的意思是:(指較近的人或事物)這霹肝,這個;(指已提到過的人或事物)這;記住這個對于理解this有幫助。

心得

  • 一般情況下this的指向只有在調(diào)用時才能確定塑煎,箭頭函數(shù)是個例外在定義是就已經(jīng)確定沫换;
  • 函數(shù)中的this指向的是函數(shù)的最終調(diào)用者(可以結(jié)合this的中文翻譯理解);
  • 函數(shù)一定是某個對象調(diào)用了才會執(zhí)行,也就是說this指向的一定是一個對象Object讯赏;

案例引入

看完幾篇大佬們講解this指向的博客垮兑,都用到了一個經(jīng)典的案例,我也班門弄斧一番漱挎,掏出這個案例琢磨一二系枪。

var a = 5
var obj = {
  a : 10,
  foo: function(){
    console.log(this.a)
  }
}

var bar = obj.foo
obj.foo() // 10
bar() // 5

案例分析

  • 案例中obj.foo()這一行的輸出結(jié)果理解起來還是非常容易的,函數(shù)的調(diào)用者是obj磕谅,所以函數(shù)中的this指向的是obj這個對象私爷,很容易想到輸出的是obj中的a值;
  • bar()這一行的輸出結(jié)果就很容讓人頭腦犯暈膊夹,在定義bar是時候明明用的是obj.foo為什么輸出結(jié)果不是obj中的a衬浑,其實(shí)var bar = obj.foo這一行只是個煙幕彈,把代碼改成下面這個樣子就比較容易理解放刨;
//這一步就類似上一段代碼中的var bar=obj.foo
var bar = function(){
    console.log(this.a)
}

  • 在定義bar的時候并沒有執(zhí)行obj.foo工秩,只是將foo這個函數(shù)體賦值給了變量bar,像上面代碼這個樣子(這樣說比較好理解进统,實(shí)際賦值的是foo函數(shù)對象的地址值)助币,這個時候執(zhí)行bar調(diào)用者是window,跟obj一點(diǎn)關(guān)系都沒有螟碎,所以輸出的是window中的a值奠支;

案例小結(jié)
通過上面這個簡單的案例(看著簡單,內(nèi)含量很大)抚芦,不難發(fā)現(xiàn)函數(shù)中this指向的是函數(shù)的調(diào)用者倍谜,關(guān)鍵點(diǎn)就是確定函數(shù)是誰調(diào)用的,那么就涉及到函數(shù)的四種綁定方式叉抡。

函數(shù)綁定

函數(shù)綁定的實(shí)質(zhì)就是函數(shù)調(diào)用尔崔,說成函數(shù)綁定顯得高大上,函數(shù)綁定有四種方式分別是:默認(rèn)綁定褥民、隱式綁定季春、現(xiàn)實(shí)綁定、new綁定消返,下面是我對這四種綁定的理解载弄。

默認(rèn)綁定

顧名思義默認(rèn)綁定就是javaScript內(nèi)部默認(rèn)的函數(shù)調(diào)用者(這句話等于沒說),發(fā)生在函數(shù)調(diào)用時沒有任何修飾情況下撵颊,不多說先來段案例代碼

var name = 'Tom'
function foo(){
  console.log('foo---'+this.name);  
}

setTimeout(function(){
  var name = 'Jerry'
  console.log('定時器---'+this.name)//定時器---Tom
  foo()//foo---Tom
},1000)
    
foo()//foo--tom

案例分析

  • 案例中最先輸出的是全局下調(diào)用的foo宇攻,非常好理解它的調(diào)用者是window,所以輸出的是window中的name值倡勇;
  • 接下來輸出的是定時器中的console.log('定時器---'+this.name)這行代碼的結(jié)果逞刷,為什么this.name輸出的不是Jerry,答案是javaScript中默認(rèn)定時器中回調(diào)函數(shù)的this是window;
  • 可以這樣理解:定時器就是一個函數(shù)夸浅,它接收兩個參數(shù)仑最,在定時器中寫函數(shù)只是一個傳參的過程,中間沒有涉及到任何改變this指向的操作(開頭提到過this指向的一定是一個對象Object)帆喇,將寫在定時器中的函數(shù)提取出來會更容易理解警医,像是下面這樣;
var name = 'Tom'
function time(){
  var name = 'Jerry'
  console.log('定時器---'+this.name)//定時器---Tom
}
    
setTimeout(time,1000)

  • 最后輸出的是定時器中調(diào)用的foo,寫這一行的目的是加強(qiáng)對默認(rèn)綁定發(fā)生在函數(shù)調(diào)用時沒有任何修飾的情況下這句話的理解坯钦。

隱式綁定

隱式綁定可以理解成書寫代碼過程中發(fā)生了函數(shù)綁定但還不自知(特別像我這個小白)预皇,還是通過案例代碼理解

var name = 'Tom'
var obj = {
  name: 'Jerry',
  f1: function () {
    console.log('f1---' + this.name);
  },
  child: {
    name: 'Speike',//Speike是只狗
    f2: function () {
      console.log('f2---' + this.name);
    },
  }
}

obj.f1()//f1---Jerry
obj.child.f2()//f2---Speike

案例分析

  • 像案例中這種通過對象調(diào)用函數(shù)的方式就是隱式綁定,函數(shù)中的this指向函數(shù)最后的調(diào)用者葫笼,還記的this的中文翻譯嗎?(指較近的人或事物)這,在這里函數(shù)的最后調(diào)用者就是調(diào)用函數(shù)時離函數(shù)名最近的那個對象深啤;
  • 豁然開朗obj.f1()的調(diào)用者就是obj拗馒,obj.child.f2()調(diào)用者就是obj.child,分析到這里輸出結(jié)果就很好理解了路星。

顯示綁定

顯示綁定就是代碼中明確的指出了函數(shù)的調(diào)用對象,通過三個方法實(shí)現(xiàn)分別是call()诱桂、apply()洋丐、bind()

call()和apply()

  • call()和apply()這兩個方法使用起來幾乎一樣,它們都會會在使用時立即執(zhí)行函數(shù)挥等,區(qū)別在于這兩個方法的傳參方式不一樣友绝;
  • call()方法接收多個參數(shù),第一個參數(shù)是this指向的對象肝劲,后續(xù)參數(shù)是函數(shù)中需要用到的所有參數(shù)迁客;
  • apply()方法只接受兩個參數(shù),第一個參數(shù)是this指向?qū)ο蟠腔保诙€參數(shù)是函數(shù)中需要用到的所有參數(shù)組成的數(shù)組掷漱。
var name = 'Tom'
  var obj = {
    name: 'Jerry',
    f1: function (a,b) {
      console.log(a+b+'f1---' + this.name);
    },
    child: {
      name: 'Speike',//Speike是只狗
      f2: function (a,b) {
        console.log(a+b+'f2---' + this.name);
      },
    }
  }

obj.child.f2('1','2')//12f2---Speike
obj.child.f2.call(obj,'1','2')//12f2---jerry
obj.child.f2.apply(obj,['1','2'])//12f2---Jerry

bind()

bind()call()apply()比較,區(qū)別在于bind()在使用時不會立即執(zhí)行函數(shù)榄檬,bind只是修改函數(shù)和的this指向卜范;

var name = 'Tom'
  var obj = {
    name: 'Jerry',
    f1: function (a,b) {
      console.log(a+b+'f1---' + this.name);
    },
    child: {
      name: 'Speike',//Speike是只狗
      f2: function (a,b) {
        console.log(a+b+'f2---' + this.name);
      },
    }
  }

var bar = obj.child.f2.bind(obj,'1','2')//使用一個變量去接收返回的結(jié)果
bar()//12f2---Jerry

new綁定

new是在通過構(gòu)造函數(shù)創(chuàng)建實(shí)例對象時使用,此時this指向的是新建的實(shí)例對象鹿榜,還是來一個案例吧比較簡潔明了海雪;

function Cat(name, age) {
  this.name = name
  this.age = age
  this.showInfo = function () {
    console.log(this.name + '---' + this.age)
  }
}

var c1 = new Cat('Tom', 5)
c1.showInfo()//Tom---5

  • 要理解new綁定,需要知道new創(chuàng)建實(shí)例對象時做了些什么
    1. 創(chuàng)建一個空對象obj
    2. 將空對象的隱式原型屬性__proto__指向構(gòu)造函數(shù)的顯示原型prototype
    3. 將this的指向obj
    4. 返回得到的實(shí)例對象

手寫函數(shù)實(shí)現(xiàn)new的功能

function myNew(p,n,a) {
  //創(chuàng)建一個空對象
  var obj = {}
  var Construct = Array.prototype.shift.call(arguments)
  //var Construct=[].shift.call(arguments)
  //上面兩段代碼效果是等效的舱殿,都是為了得到Cat這個構(gòu)造函數(shù)奥裸,這一步是難點(diǎn)
  //這里arguments的細(xì)節(jié)不是很明白,需要去弄明白
  obj.__proto__ = Construct.prototype

  Construct.apply(obj,arguments)//更改this的指向
  return obj//返回得到的新對象
}

function Cat(name, age) {
  this.name = name
  this.age = age
  this.showInfo = function () {
    console.log(this.name + '---' + this.age)
  }
}

var c1 = myNew(Cat,'Tom',20)
console.log(c1)//Cat {name: "Tom", age: 20, showInfo: ?}得到了與new一樣的效果

案例小結(jié)
經(jīng)過手寫函數(shù)實(shí)現(xiàn)new的功能沪袭,就能夠理解new綁定this指向的是新建的實(shí)例對象刺彩,在這個過程中發(fā)現(xiàn)對于arguments理解不透徹,留著下次總結(jié)。

函數(shù)綁定的優(yōu)先級

經(jīng)過上面這一番折騰创倔,可算是把函數(shù)綁定這件是說清楚了嗡害,還有非常重要一點(diǎn)上面這四種函數(shù)綁定方式存在優(yōu)先級:new綁定>顯示綁定>隱式綁定>默認(rèn)綁定

箭頭函數(shù)

箭頭函數(shù)是ES6中新增的一種定義函數(shù)的方式,對于箭頭函數(shù)中的this指向記住以下幾個要點(diǎn):

  • 箭頭函數(shù)中沒有自己的this畦攘,箭頭函數(shù)中的this是的定義函數(shù)時上一層函數(shù)中的this霸妹;
  • 箭頭函數(shù)中的this在書寫代碼時就已經(jīng)確定,這有別于之前提到的函數(shù)中的this是在調(diào)用時確定的知押;
  • 箭頭函數(shù)中的沒有call()叹螟、apply()、bind()這三個方法台盯,當(dāng)然也就不能更改箭頭函數(shù)中this的指向罢绽;
  • 箭頭函數(shù)中沒有構(gòu)造函數(shù),不能使用new綁定静盅。
var name = 'Tom'
var obj = {
    name: 'Jerry'
}

; (function () {
  setTimeout(() => {
    console.log(this.name);
  }, 1000)
}).call(obj)//Jerry
//使用call方法改變了上一層函數(shù)中this的指向

關(guān)于this指向問題的總結(jié)就到這里了良价,歡迎評論指正,共同學(xué)習(xí)互相勉勵]锏C鞴浮!
新手小白市咽,大佬勿噴痊银,謝謝!施绎!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末溯革,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谷醉,更是在濱河造成了極大的恐慌致稀,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孤紧,死亡現(xiàn)場離奇詭異豺裆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)号显,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門臭猜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人押蚤,你說我怎么就攤上這事蔑歌。” “怎么了揽碘?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵次屠,是天一觀的道長园匹。 經(jīng)常有香客問我,道長劫灶,這世上最難降的妖魔是什么裸违? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮本昏,結(jié)果婚禮上供汛,老公的妹妹穿的比我還像新娘。我一直安慰自己涌穆,他們只是感情好怔昨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宿稀,像睡著了一般趁舀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上祝沸,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天矮烹,我揣著相機(jī)與錄音,去河邊找鬼奋隶。 笑死擂送,一個胖子當(dāng)著我的面吹牛悦荒,可吹牛的內(nèi)容都是我干的唯欣。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼搬味,長吁一口氣:“原來是場噩夢啊……” “哼境氢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碰纬,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤萍聊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后悦析,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寿桨,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年强戴,在試婚紗的時候發(fā)現(xiàn)自己被綠了亭螟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡骑歹,死狀恐怖预烙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情道媚,我是刑警寧澤扁掸,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布翘县,位于F島的核電站,受9級特大地震影響谴分,放射性物質(zhì)發(fā)生泄漏锈麸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一牺蹄、第九天 我趴在偏房一處隱蔽的房頂上張望掐隐。 院中可真熱鬧,春花似錦钞馁、人聲如沸虑省。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽探颈。三九已至,卻和暖如春训措,著一層夾襖步出監(jiān)牢的瞬間伪节,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工绩鸣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留怀大,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓呀闻,卻偏偏與公主長得像化借,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捡多,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350

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