JavaScript中的this陷阱

JavaScript來(lái)自一門(mén)健全的語(yǔ)言聂抢,所以你可能覺(jué)得JavaScript中的this和其他面向?qū)ο蟮恼Z(yǔ)言如java的this一樣冯凹,是指存儲(chǔ)在實(shí)例屬性中的值龟劲。事實(shí)并非如此,在JavaScript中予借,最好把this當(dāng)成哈利波特中的博格特的背包傅是,有著深不可測(cè)的魔力。

JavaScript中很多時(shí)候會(huì)用到this蕾羊,下面詳細(xì)介紹每一種情況喧笔。在這里我想首先介紹一下宿主環(huán)境這個(gè)概念。一門(mén)語(yǔ)言在運(yùn)行的時(shí)候龟再,需要一個(gè)環(huán)境书闸,叫做宿主環(huán)境。對(duì)于JavaScript利凑,宿主環(huán)境最常見(jiàn)的是web瀏覽器浆劲,瀏覽器提供了一個(gè)JavaScript運(yùn)行的環(huán)境,這個(gè)環(huán)境里面哀澈,需要提供一些接口牌借,好讓JavaScript引擎能夠和宿主環(huán)境對(duì)接。JavaScript引擎才是真正執(zhí)行JavaScript代碼的地方割按,常見(jiàn)的引擎有V8(目前最快JavaScript引擎膨报、Google生產(chǎn))、JavaScript core适荣。JavaScript引擎主要做了下面幾件事情:

  • 一套與宿主環(huán)境相聯(lián)系的規(guī)則;
  • JavaScript引擎內(nèi)核(基本語(yǔ)法規(guī)范现柠、邏輯、命令和算法);
  • 一組內(nèi)置對(duì)象和API;
  • 其他約定弛矛。

但是環(huán)境不是唯一的够吩,也就是JavaScript不僅僅能夠在瀏覽器里面跑,也能在其他提供了宿主環(huán)境的程序里面跑丈氓,最常見(jiàn)的就是nodejs周循。同樣作為一個(gè)宿主環(huán)境强法,nodejs也有自己的JavaScript引擎–V8。根據(jù)官方的定義:
Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications

global this
  • 在瀏覽器里湾笛,在全局范圍內(nèi)拟烫,this等價(jià)于window對(duì)象。
<script type="text/javascript">
        console.log(this === window); //true
</script>
  • 在瀏覽器里迄本,在全局范圍內(nèi),用var聲明一個(gè)變量和給this或者window添加屬性是等價(jià)的课竣。
<script type="text/javascript">
        var foo = "bar";
        console.log(this.foo); //logs "bar"
        console.log(window.foo); //logs "bar"
</script>
  • 如果你在聲明一個(gè)變量的時(shí)候沒(méi)有使用var或者let(ECMAScript 6),你就是在給全局的this添加或者改變屬性值嘉赎。
<script type="text/javascript">
        foo = "bar";
        function testThis() {
              foo = "foo";
        }
        console.log(this.foo); //logs "bar"
        testThis();
        console.log(this.foo); //logs "foo"
</script>
  • 在node環(huán)境里,如果使用REPL(Read-Eval-Print Loop于樟,簡(jiǎn)稱(chēng)REPL:讀取-求值-輸出,是一個(gè)簡(jiǎn)單的公条,交互式的編程環(huán)境)來(lái)執(zhí)行程序,this并不是最高級(jí)的命名空間,最高級(jí)的是global.
> this
{ ArrayBuffer: [Function: ArrayBuffer],
  Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
  Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
  ...
> global === this
true
  • 在node環(huán)境里迂曲,如果執(zhí)行一個(gè)js腳本靶橱,在全局范圍內(nèi),this以一個(gè)空對(duì)象開(kāi)始作為最高級(jí)的命名空間路捧,這個(gè)時(shí)候关霸,它和global不是等價(jià)的。
test.js腳本內(nèi)容:
console.log(this);
console.log(this === global);
REPL運(yùn)行腳本:
$ node test.js
{}
false
  • 在node環(huán)境里杰扫,在全局范圍內(nèi)队寇,如果你用REPL執(zhí)行一個(gè)腳本文件,用var聲明一個(gè)變量并不會(huì)和在瀏覽器里面一樣將這個(gè)變量添加給this章姓。
test.js:
var foo = "bar";
console.log(this.foo);
$ node test.js
undefined
  • 但是如果你不是用REPL執(zhí)行腳本文件佳遣,而是直接執(zhí)行代碼,結(jié)果和在瀏覽器里面是一樣的(神坑)凡伊。
> var foo = "bar";
> this.foo
bar
> global.foo
bar
  • 在node環(huán)境里零渐,用REPL運(yùn)行腳本文件的時(shí)候,如果在聲明變量的時(shí)候沒(méi)有使用var或者let系忙,這個(gè)變量會(huì)自動(dòng)添加到global對(duì)象诵盼,但是不會(huì)自動(dòng)添加給this對(duì)象。如果是直接執(zhí)行代碼银还,則會(huì)同時(shí)添加給global和this
test.js
foo = "bar";
console.log(this.foo);
console.log(global.foo);
$ node test.js
undefined
bar

上面的八種情況可能大家已經(jīng)繞暈了拦耐,總結(jié)起來(lái)就是:在瀏覽器里面this是老大,它等價(jià)于window對(duì)象见剩,如果你聲明一些全局變量(不管在任何地方)杀糯,這些變量都會(huì)作為this的屬性。在node里面苍苞,有兩種執(zhí)行JavaScript代碼的方式固翰,一種是直接執(zhí)行寫(xiě)好的JavaScript文件狼纬,另外一種是直接在里面執(zhí)行一行行代碼。對(duì)于直接運(yùn)行一行行JavaScript代碼的方式骂际,global才是老大疗琉,this和它是等價(jià)的。在這種情況下歉铝,和瀏覽器比較相似盈简,也就是聲明一些全局變量會(huì)自動(dòng)添加給老大global,順帶也會(huì)添加給this太示。但是在node里面直接腳本文件就不一樣了柠贤,你聲明的全局變量不會(huì)自動(dòng)添加到this,但是會(huì)添加到global對(duì)象类缤。所以相同點(diǎn)是臼勉,在全局范圍內(nèi),全局變量終究是屬于老大的餐弱。

function this

無(wú)論是在瀏覽器環(huán)境還是node環(huán)境宴霸, 除了在DOM事件處理程序里或者給出了thisArg(接下來(lái)會(huì)講到)外,如果不是用new調(diào)用膏蚓,在函數(shù)里面使用this都是指代全局范圍的this瓢谢。

<script type="text/javascript">
      foo = "bar"; 
      function testThis() {
           this.foo = "foo";
      }
      console.log(this.foo); //logs "bar"
      testThis();
      console.log(this.foo); //logs "foo"
</script>
test.js
 
foo = "bar";
function testThis () {
    this.foo = "foo";
}
console.log(global.foo);
testThis();
console.log(global.foo);
$ node test.js
bar
foo
  • 除非你使用嚴(yán)格模式,這時(shí)候this就會(huì)變成undefined驮瞧。
<script type="text/javascript">
      foo = "bar";
      function testThis() {
          "use strict";
          this.foo = "foo";
      }
      console.log(this.foo); //logs "bar"
      testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
</script>

我更喜歡把新的值稱(chēng)作一個(gè)實(shí)例恩闻。

函數(shù)里面的this其實(shí)相對(duì)比較好理解,如果我們?cè)谝粋€(gè)函數(shù)里面使用this剧董,需要注意的就是我們調(diào)用函數(shù)的方式幢尚,如果是正常的方式調(diào)用函數(shù),this指代全局的this翅楼,如果我們加一個(gè)new尉剩,這個(gè)函數(shù)就變成了一個(gè)構(gòu)造函數(shù),我們就創(chuàng)建了一個(gè)實(shí)例毅臊,this指代這個(gè)實(shí)例理茎,這個(gè)和其他面向?qū)ο蟮恼Z(yǔ)言很像。另外管嬉,寫(xiě)JavaScript很常做的一件事就是綁定事件處理程序皂林,也就是諸如button.addEventListener(‘click’, fn, false)之類(lèi)的,如果在fn里面需要使用this蚯撩,this指代事件處理程序?qū)?yīng)的對(duì)象础倍,也就是button。

時(shí)間有限胎挎,未完待續(xù)沟启。忆家。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末德迹,一起剝皮案震驚了整個(gè)濱河市芽卿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胳搞,老刑警劉巖卸例,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異肌毅,居然都是意外死亡筷转,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)芽腾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人页衙,你說(shuō)我怎么就攤上這事摊滔。” “怎么了店乐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵艰躺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我眨八,道長(zhǎng)腺兴,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任廉侧,我火速辦了婚禮页响,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘段誊。我一直安慰自己闰蚕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布连舍。 她就那樣靜靜地躺著没陡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪索赏。 梳的紋絲不亂的頭發(fā)上盼玄,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音潜腻,去河邊找鬼埃儿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛融涣,可吹牛的內(nèi)容都是我干的蝌箍。 我是一名探鬼主播青灼,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼妓盲!你這毒婦竟也來(lái)了杂拨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悯衬,失蹤者是張志新(化名)和其女友劉穎弹沽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體筋粗,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡策橘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了娜亿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丽已。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖买决,靈堂內(nèi)的尸體忽然破棺而出沛婴,到底是詐尸還是另有隱情,我是刑警寧澤督赤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布嘁灯,位于F島的核電站,受9級(jí)特大地震影響躲舌,放射性物質(zhì)發(fā)生泄漏丑婿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一没卸、第九天 我趴在偏房一處隱蔽的房頂上張望羹奉。 院中可真熱鬧,春花似錦约计、人聲如沸尘奏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炫加。三九已至,卻和暖如春铺然,著一層夾襖步出監(jiān)牢的瞬間俗孝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工魄健, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赋铝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓沽瘦,卻偏偏與公主長(zhǎng)得像革骨,于是被迫代替她去往敵國(guó)和親农尖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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