JavaScript中的this

前言

總括:詳解JavaScript中的this的一篇總結(jié),不懂this這個(gè)難點(diǎn)渊鞋,很多時(shí)候會(huì)造成一些困擾绰更,寫出一些bug不知如何收場,所以一起來寫bug吧锡宋,不對儡湾,一起來寫代碼吧。

人生得意須盡歡执俩,莫使金樽空對月

正文

? JavaScript中的this格外的不一樣徐钠,比如Java語言中的this是在代碼的執(zhí)行階段是不可更改,而JavaScript的this是在調(diào)用階段進(jìn)行綁定役首。??因?yàn)檫@一性質(zhì)所以給了this很大的發(fā)揮空間尝丐。但其在嚴(yán)格模式和非嚴(yán)格模式下又有些不一樣,在函數(shù)的不同調(diào)用方式也導(dǎo)致this有些區(qū)別衡奥。??

What's this?

??首先對this的下個(gè)定義:this是在執(zhí)行上下文創(chuàng)建時(shí)確定的一個(gè)在執(zhí)行過程中不可更改的變量爹袁。

所謂執(zhí)行上下文,就是JavaScript引擎在執(zhí)行一段代碼之前將代碼內(nèi)部會(huì)用到的一些變量矮固、函數(shù)失息、this提前聲明然后保存在變量對象中的過程。這個(gè)'代碼片段'包括:全局代碼(script標(biāo)簽內(nèi)部的代碼)、函數(shù)內(nèi)部代碼盹兢、eval內(nèi)部代碼邻梆。而我們所熟知的作用域鏈也會(huì)在保存在這里,以一個(gè)類數(shù)組的形式存儲在對應(yīng)函數(shù)的[[Scopes]]屬性中蛤迎。

this只在函數(shù)調(diào)用階段確定确虱,也就是執(zhí)行上下文創(chuàng)建的階段進(jìn)行賦值,保存在變量對象中替裆。這個(gè)特性也導(dǎo)致了this的多變性:??即當(dāng)函數(shù)在不同的調(diào)用方式下都可能會(huì)導(dǎo)致this的值不同校辩。

????上面我們說過了在嚴(yán)格模式下和非嚴(yán)格模式下this表現(xiàn)不同:

var a = 1;
function fun() {
   'use strict';
    var a = 2;
    return this.a;
}
fun();//??報(bào)錯(cuò) Cannot read property 'a' of undefined

??嚴(yán)格模式下,this指向undefined;

var a = 1;
function fun() {
    var a = 2;
    return this.a;
}
fun();//1

??非嚴(yán)格模式下this指向window;

上面同一段代碼辆童,在不同模式下之所以有不同表現(xiàn)宜咒,就是因?yàn)閠his在嚴(yán)格模式,非嚴(yán)格模式下的不同把鉴。

結(jié)論:當(dāng)函數(shù)獨(dú)立調(diào)用的時(shí)候故黑,在嚴(yán)格模式下它的this指向undefined,在非嚴(yán)格模式下庭砍,當(dāng)this指向undefined的時(shí)候场晶,自動(dòng)指向全局對象(瀏覽器中就是window)

多提一句,在全局環(huán)境下怠缸,this就是指向自己诗轻,再看??:

this.a = 1;
var b = 1;
c = 1;
console.log(this === window)//true
//這三種都能得到想要的結(jié)果,全局上下文的變量對象中存在這三個(gè)變量

再多提一句揭北,當(dāng)this不在函數(shù)中用的時(shí)候會(huì)怎樣?看??:

var a = 1000;
var obj = {
    a: 1,
    b: this.a + 1
}
function fun() {
    var obj = {
        a: 1,
        c: this.a + 2 //嚴(yán)格模式下這塊報(bào)錯(cuò) Cannot read property 'a' of undefined
    }
    return obj.c;
}
console.log(fun());//1002
console.log(obj.b);//1001

這種情況下this還是指向了window扳炬。那么我們可以單獨(dú)下個(gè)結(jié)論:

當(dāng)obj在全局聲明的時(shí)候,obj內(nèi)部屬性中的this指向全局對象搔体,當(dāng)obj在一個(gè)函數(shù)中聲明的時(shí)候恨樟,嚴(yán)格模式下this會(huì)指向undefined,非嚴(yán)格模式自動(dòng)轉(zhuǎn)為指向全局對象疚俱。

??好了劝术,剛剛小試牛刀下,知道了嚴(yán)格模式和非嚴(yán)格模式下this的區(qū)別呆奕,然而我們?nèi)粘?yīng)用最多的還是在函數(shù)中用this夯尽,上面也說過了this在函數(shù)的不同調(diào)用方式還有區(qū)別,那么函數(shù)的調(diào)用方式都有哪些呢登馒?四種:

  • 在全局環(huán)境或是普通函數(shù)中直接調(diào)用
  • 作為對象的方法
  • 使用apply和call
  • 作為構(gòu)造函數(shù)

下面分別就四種情況展開:

直接調(diào)用

上面的??其實(shí)就是直接調(diào)用的,不過我決定再寫????:

var a = 1;
var obj  =  {
    a: 2,
    b: function () {
        function fun() {
          return this.a
        }
       console.log(fun());
    }
} 
obj.b();//1

fun函數(shù)雖然在obj.b方法中定義咆槽,但它還是一個(gè)普通函數(shù)陈轿,直接調(diào)用在非嚴(yán)格模式下指向undefined,又自動(dòng)指向了全局對象,正如預(yù)料麦射,嚴(yán)格模式會(huì)報(bào)錯(cuò)undefined.a不成立蛾娶,a未定義。

重要的事情再說一遍:當(dāng)函數(shù)獨(dú)立調(diào)用的時(shí)候潜秋,在嚴(yán)格模式下它的this指向undefined蛔琅,在非嚴(yán)格模式下,當(dāng)this指向undefined的時(shí)候峻呛,自動(dòng)指向全局對象(瀏覽器中就是window)罗售。??

作為對象的方法

var a = 1;
var obj = {
  a: 2,
  b: function() {
    return this.a;
  }
}
console.log(obj.b())//2

??b所引用的匿名函數(shù)作為obj的一個(gè)方法調(diào)用,這時(shí)候this指向調(diào)用它的對象钩述。這里也就是obj寨躁。那么如果b方法不作為對象方法調(diào)用呢?啥意思呢牙勘,就是這樣??:

var a = 1;
var obj = {
  a: 2,
  b: function() {
    return this.a;
  }
}
var t = obj.b;
console.log(t());//1

如上职恳,t函數(shù)執(zhí)行結(jié)果竟然是全局變量1,為啥呢方面?這就涉及Javascript的內(nèi)存空間了放钦,就是說,obj對象的b屬性存儲的是對該匿名函數(shù)的一個(gè)引用恭金,可以理解為一個(gè)指針操禀。當(dāng)賦值給t的時(shí)候,并沒有單獨(dú)開辟內(nèi)存空間存儲新的函數(shù)蔚叨,而是讓t存儲了一個(gè)指針床蜘,該指針指向這個(gè)函數(shù)。相當(dāng)于執(zhí)行了這么一段偽代碼:

var a = 1;
function fun() {//此函數(shù)存儲在堆中
    return this.a;
}
var obj = {
  a: 2,
  b: fun //b指向fun函數(shù)
}
var t = fun;//變量t指向fun函數(shù)
console.log(t());//1

此時(shí)的t就是一個(gè)指向fun函數(shù)的指針蔑水,調(diào)用t邢锯,相當(dāng)于直接調(diào)用fun,套用以上規(guī)則搀别,打印出來1自然很好理解了丹擎。

使用apply,call

關(guān)于apply和call是干什么的怎么用本文不涉及,請移駕:apply歇父,call

這是個(gè)萬能公式蒂培,實(shí)際上上面直接調(diào)用的代碼,我們可以看成這樣的:

function fun() {
    return this.a;
}
fun();//1
//嚴(yán)格模式
fun.call(undefined)
//非嚴(yán)格模式
fun.call(window)

這時(shí)候我們就可以解釋下榜苫,為啥說在非嚴(yán)格模式下护戳,當(dāng)函數(shù)this指向undefined的時(shí)候,會(huì)自動(dòng)指向全局對象垂睬,如上媳荒,在非嚴(yán)格模式下抗悍,當(dāng)調(diào)用fun.call(undefined)的時(shí)候打印出來的依舊是1,就是最好的證據(jù)钳枕。

為啥說是萬能公式呢缴渊?再看函數(shù)作為對象的方法調(diào)用:

var a = 1;
var obj = {
  a: 2,
  b: function() {
    return this.a;
  }
}
obj.b()
obj.b.call(obj)

如上,是不是很強(qiáng)大鱼炒,可以理解為其它兩種都是這個(gè)方法的語法糖罷了衔沼,那么apply和call是不是真的萬能的呢?并不是昔瞧,ES6的箭頭函數(shù)就是特例指蚁,因?yàn)榧^函數(shù)的this不是在調(diào)用時(shí)候確定的,這也就是為啥說箭頭函數(shù)好用的原因之一硬爆,因?yàn)樗膖his固定不會(huì)變來變?nèi)サ牧诵蓝妗jP(guān)于箭頭函數(shù)的this我們稍后再說。

作為構(gòu)造函數(shù)

何為構(gòu)造函數(shù)缀磕?所謂構(gòu)造函數(shù)就是用來new對象的函數(shù)缘圈,像FunctionObject袜蚕、Array糟把、Date等都是全局定義的構(gòu)造函數(shù)。其實(shí)每一個(gè)函數(shù)都可以new對象牲剃,那些批量生產(chǎn)我們需要的對象的函數(shù)就叫它構(gòu)造函數(shù)罷了遣疯。注意,構(gòu)造函數(shù)首字母記得大寫凿傅。

function Fun() {
  this.name = 'Damonre';
  this.age = 21;
  this.sex = 'man';
  this.run = function () {
    return this.name + '正在跑步';
  }
}
Fun.prototype = {
  contructor: Fun,
  say: function () {
    return this.name + '正在說話';
  }
}
var f = new Fun();
f.run();//Damonare正在跑步
f.say();//Damonare正在說話

如上缠犀,如果函數(shù)作為構(gòu)造函數(shù)用,那么其中的this就代表它即將new出來的對象聪舒。為啥呢辨液?new做了啥呢?

偽代碼如下:

function Fun() {
  //new做的事情
  var obj = {};
  obj.__proto__ = Fun.prototype;//Base為構(gòu)造函數(shù)
  obj.name = 'Damonare';
  ...//一系列賦值以及更多的事
  return obj
}

也就是說new做了下面這些事:

  • 創(chuàng)建一個(gè)臨時(shí)對象
  • 給臨時(shí)對象綁定原型
  • 給臨時(shí)對象對應(yīng)屬性賦值
  • 將臨時(shí)對象return

也就是說new其實(shí)就是個(gè)語法糖箱残,this之所以指向臨時(shí)對象還是沒逃脫上面說的幾種情況滔迈。

當(dāng)然如果直接調(diào)用Fun(),如下:

function Fun() {
  this.name = 'Damonre';
  this.age = 21;
  this.sex = 'man';
  this.run = function () {
    return this.name + '正在跑步';
  }
}
Fun();
console.log(window)

其實(shí)就是直接調(diào)用一個(gè)函數(shù),this在非嚴(yán)格模式下指向window被辑,你可以在window對象找到所有的變量燎悍。

另外還有一點(diǎn),prototype對象的方法的this指向?qū)嵗龑ο笈卫恚驗(yàn)閷?shí)例對象的__proto__已經(jīng)指向了原型函數(shù)的prototype谈山。這就涉及原型鏈的知識了,即方法會(huì)沿著對象的原型鏈進(jìn)行查找宏怔。

箭頭函數(shù)

剛剛提到了箭頭函數(shù)是一個(gè)不可以用call和apply改變this的典型奏路。

我們看下面這個(gè)??:

var a = 1;
var obj = {
  a: 2
};
var fun = () => console.log(this.a);
fun();//1
fun.call(obj)//1

以上抗蠢,兩次調(diào)用都是1。

那么箭頭函數(shù)的this是怎么確定的呢思劳?箭頭函數(shù)會(huì)捕獲其所在上下文的 this 值,作為自己的 this妨猩,也就是說箭頭函數(shù)的this在詞法層面就完成了綁定潜叛。apply,call方法只是傳入?yún)?shù)壶硅,卻改不了this威兜。

var a = 1;
var obj = {
  a: 2
};
function fun() {
    var a = 3;
    let f = () => console.log(this.a);
    f();
};
fun();//1
fun.call(obj);//2

如上,fun直接調(diào)用庐椒,fun的上下文中的this值為window椒舵,注意,這個(gè)地方有點(diǎn)繞约谈。fun的上下文就是此箭頭函數(shù)所在的上下文笔宿,因此此時(shí)f的this為fun的this也就是window。當(dāng)fun.call(obj)再次調(diào)用的時(shí)候棱诱,新的上下文創(chuàng)建泼橘,fun此時(shí)的this為obj,也就是箭頭函數(shù)的this值迈勋。

再來一個(gè)??:

function Fun() {
    this.name = 'Damonare';
}
Fun.prototype.say = () => {
    console.log(this);
}
var f = new Fun();
f.say();//window

有的同學(xué)看到這個(gè)??會(huì)很懵逼炬灭,感覺上this應(yīng)該指向f這個(gè)實(shí)例對象啊。不是的靡菇,此時(shí)的箭頭函數(shù)所在的上下文是__proto__所在的上下文也就是Object函數(shù)的上下文重归,而Object的this值就是全局對象。

那么再來一個(gè)??:

function Fun() {
    this.name = 'Damonare';
    this.say = () => {
        console.log(this);
    }
}
var f = new Fun();
f.say();//Fun的實(shí)例對象

如上厦凤,this.say所在的上下文鼻吮,此時(shí)箭頭函數(shù)所在的上下文就變成了Fun的上下文環(huán)境,而因?yàn)樯厦嬲f過當(dāng)函數(shù)作為構(gòu)造函數(shù)調(diào)用的時(shí)候(也就是new的作用)上下文環(huán)境的this指向?qū)嵗龑ο蟆?/p>

后記

文中定義均為個(gè)人總結(jié)泳唠,不妥之處還請雅正狈网。

轉(zhuǎn)載請注明出處。

以上笨腥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拓哺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子脖母,更是在濱河造成了極大的恐慌士鸥,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谆级,死亡現(xiàn)場離奇詭異烤礁,居然都是意外死亡讼积,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門脚仔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勤众,“玉大人,你說我怎么就攤上這事鲤脏∶茄眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵猎醇,是天一觀的道長窥突。 經(jīng)常有香客問我,道長硫嘶,這世上最難降的妖魔是什么阻问? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮沦疾,結(jié)果婚禮上称近,老公的妹妹穿的比我還像新娘。我一直安慰自己曹鸠,他們只是感情好煌茬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著彻桃,像睡著了一般坛善。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邻眷,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天眠屎,我揣著相機(jī)與錄音,去河邊找鬼肆饶。 笑死改衩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驯镊。 我是一名探鬼主播葫督,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼板惑!你這毒婦竟也來了橄镜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤冯乘,失蹤者是張志新(化名)和其女友劉穎洽胶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裆馒,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡姊氓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年丐怯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翔横。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡读跷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出禾唁,到底是詐尸還是另有隱情舔亭,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布蟀俊,位于F島的核電站,受9級特大地震影響订雾,放射性物質(zhì)發(fā)生泄漏肢预。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一洼哎、第九天 我趴在偏房一處隱蔽的房頂上張望烫映。 院中可真熱鬧,春花似錦噩峦、人聲如沸锭沟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽族淮。三九已至,卻和暖如春凭涂,著一層夾襖步出監(jiān)牢的瞬間祝辣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工切油, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝙斜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓澎胡,卻偏偏與公主長得像孕荠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子攻谁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

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

  • 導(dǎo)語 不得不說稚伍,作為一名初級的前端開發(fā)者,this關(guān)鍵字這個(gè)問題對于我來說一直是一個(gè)痛點(diǎn)巢株,什么是this槐瑞?什么是函...
    Nicole_tiny閱讀 530評論 0 4
  • this在JavaScript中似乎可以視而不見,但不去正視它學(xué)到的就只是殘缺的JS阁苞。拋開這些“形而上”的意義不說...
    棕小漸閱讀 307評論 0 2
  • 不論是面向?qū)ο罄ч荩€是基于對象的語言祠挫,都會(huì)有this,我更喜歡叫他this指針悼沿,如果你不理解指針等舔,認(rèn)為它是個(gè)引用也無...
    faremax閱讀 680評論 2 1
  • 本來不想寫this的東西,因?yàn)閷?shí)在是頭暈啊糟趾,講不清楚慌植,JavaScript中的this真是讓人抓狂,好在我們調(diào)試的...
    轉(zhuǎn)角遇見一直熊閱讀 1,881評論 11 39
  • 距離3017還有兩天义郑,覺得自己不能在這么胖下去了蝶柿,我的青春要這么胖下去嗎,我不要我也不想非驮,我要減肥交汤,我要減肥,我要...
    顧陌涵閱讀 210評論 0 0